<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">Blog posts by Vipin Banka | Learnings & Insights</title><link href="http://world.optimizely.com" /><updated>2026-07-31T03:15:37.0000000Z</updated><id>https://world.optimizely.com/blogs/vipin-banka--learnings--insights/</id> <generator uri="http://world.optimizely.com" version="2.0">Optimizely World</generator> <entry><title>Flat or Nested? Weighing Your Content Modeling Options in Optimizely SaaS CMS</title><link href="https://world.optimizely.com/blogs/vipin-banka--learnings--insights/dates/2026/7/flat-or-nested-weighing-your-content-modeling-options-in-optimizely-saas-cms/" /><id>&lt;p&gt;When building a headless site on Optimizely SaaS CMS, one of the earliest and most critical design milestones your team will face is defining your &lt;strong&gt;content model&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Unlike traditional monolithic setups where visual layout often dictates data structure, headless modeling requires you to design a clear, reusable contract between two very different groups of people: your &lt;strong&gt;content authors&lt;/strong&gt; (working in the CMS UI) and your &lt;strong&gt;developers&lt;/strong&gt; (writing front-end components).&lt;/p&gt;
&lt;p&gt;Because developers are trained to eliminate duplicate code (the &quot;DRY&quot; principle), they often instinctively design highly complex, nested, or consolidated data structures. Authors, on the other hand, require flat, intuitive, and self-explanatory editing interfaces.&lt;/p&gt;
&lt;p&gt;When these two instincts collide, tension arises.&lt;/p&gt;
&lt;p&gt;Below, we&amp;rsquo;ll walk through a common, real-world scenario&amp;mdash;modeling standard videos versus gated lead-generation videos&amp;mdash;and lay out three valid modeling options, complete with their architectural trade-offs, so your team can make a well-informed choice.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The Scenario: One Concept, Two Realities&lt;/h2&gt;
&lt;p&gt;Let&#39;s say your site needs to support videos in two very different business contexts:&lt;/p&gt;
&lt;ol class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Standard Video:&lt;/strong&gt; An editorial or informational video with autoplay, looping, and visual player controls.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Gated Video:&lt;/strong&gt; A lead-generation asset that plays a brief preview, pauses at a specific timestamp, and displays an email capture form synced automatically with an external Marketing Automation Platform (MAP).&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; border-width: 1px; border-style: solid;&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border-width: 1px; border-style: solid;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Feature&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border-width: 1px; border-style: solid;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Standard Video&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border-width: 1px; border-style: solid;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Gated Video&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Author sets&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;Video file, Poster image, Title, Autoplay toggle, Loop toggle, Show controls&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;Video file, Poster image, Title&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Sourced from MAP&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;Not applicable&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;Form ID, Gate Timestamp (seconds to trigger form)&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Front-end behavior&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;Continuous playback, native player controls&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;Pauses playback at Gate time and overlays lead capture form&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;To model this, your team has three distinct structural approaches to consider. Let&#39;s look at the trade-offs of each.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Option 1: The Consolidated &quot;Mega-Block&quot; (All Fields in One Type)&lt;/h2&gt;
&lt;p&gt;This approach creates a single, highly flexible content type&amp;mdash;such as VideoComponent&amp;mdash;containing every possible property. You then rely on the author&#39;s training (or inline help text) to know which fields to fill.&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code&gt;Consolidated Block: VideoComponent
├── VideoFile
├── PosterImage
├── Title
├── Autoplay         (Fill only for Standard)
├── Loop             (Fill only for Standard)
├── ShowControls     (Fill only for Standard)
├── FormId           (Fill only for Gated)
└── GateTimestamp    (Fill only for Gated)&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;The Trade-Offs&lt;/h3&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Simple Setup:&lt;/strong&gt; Only one content type to define, register, and sync to the CMS.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Single Component:&lt;/strong&gt; Developers maintain just one straightforward React component with simple conditional rendering.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Author Cognitive Load:&lt;/strong&gt; Authors are forced to look at &quot;dead fields.&quot; An editor adding a gated marketing video must mentally ignore Autoplay, Loop, and Controls.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Validation Gaps:&lt;/strong&gt; You cannot easily make fields like FormId required for gated videos without making them required for standard videos too, risking incomplete data.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Option 2: The Nested &quot;Wrapper Block&quot; (Composition via Content Area)&lt;/h2&gt;
&lt;p&gt;This approach seeks to avoid database field duplication by pulling the shared properties (&lt;strong&gt;VideoFile&lt;/strong&gt;, &lt;strong&gt;PosterImage&lt;/strong&gt;, and &lt;strong&gt;Title&lt;/strong&gt;) into a parent &lt;strong&gt;VideoItem &lt;/strong&gt;block. This block then contains a nested Content Area where the author inserts a secondary &quot;extension block&quot; containing the specific properties.&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code&gt;Parent Block: VideoItem
├── VideoFile
├── PosterImage
├── Title
└── Content Area (Allows only StandardProperties OR GatedProperties)&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;The Trade-Offs&lt;/h3&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Strictly &quot;DRY&quot; Database Schema:&lt;/strong&gt; The base fields are declared exactly once, preventing schema duplication.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Modularity:&lt;/strong&gt; You can reuse the &quot;Standard&quot; or &quot;Gated&quot; property blocks in other components across the system.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Configurable guardrails:&lt;/strong&gt; Content Areas are not a free-for-all. Built-in validation fields let you constrain them declaratively&amp;mdash;&lt;strong&gt;allowedTypes &lt;/strong&gt;/ &lt;strong&gt;restrictedTypes &lt;/strong&gt;restrict exactly which extension types can be dropped in, and &lt;strong&gt;maxLength: 1&lt;/strong&gt; caps the area to a single item&amp;mdash;so wrong-type and duplicate insertions are preventable without any custom code.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Editing Friction:&lt;/strong&gt; Authors must execute a two-step process to build a single video. They fill out the parent, click &quot;Create New Block&quot; inside the Content Area, and then fill out the child block&amp;mdash;slower than picking one flat type up front.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The &quot;empty wrapper&quot; gap:&lt;/strong&gt; While &lt;strong&gt;allowedTypes &lt;/strong&gt;and &lt;strong&gt;maxLength &lt;/strong&gt;handle type and cardinality, enforcing a strict &lt;em&gt;minimum&lt;/em&gt; of one item is not cleanly supported for content areas&amp;mdash;so an author can still save an empty wrapper with no extension inside.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;GraphQL Query Noise:&lt;/strong&gt; Even when correctly constrained, every query to fetch a video must navigate a nested fragment structure, increasing the payload and query complexity compared to a flat type.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Option 3: The Split-Type &quot;Symmetrical&quot; Pattern (Recommended)&lt;/h2&gt;
&lt;p&gt;This approach splits the concept into two distinct, flat content types: &lt;strong&gt;StandardVideo &lt;/strong&gt;and &lt;strong&gt;GatedVideo&lt;/strong&gt;. The author&#39;s choice of which component to insert in their page layout &lt;em&gt;is&lt;/em&gt; the disambiguation.&lt;/p&gt;
&lt;p&gt;To keep things clean, we use Optimizely&#39;s built-in features to resolve the duplication: &lt;strong&gt;Property Groups&lt;/strong&gt; for authors, and &lt;strong&gt;SDK Contracts&lt;/strong&gt; for developers.&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code&gt;Standard Video (Type 1)
├── [Video Settings Group]   &amp;larr; via SDK Contract
│     ├── VideoFile
│     ├── PosterImage
│     └── Title
└── [Player Options Group]
      ├── Autoplay, Loop, ShowControls

Gated Video (Type 2)
├── [Video Settings Group]   &amp;larr; via SDK Contract
│     ├── VideoFile
│     ├── PosterImage
│     └── Title
└── [Lead Capture Group]
      ├── FormId, GateTimestamp&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;The Trade-Offs&lt;/h3&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Excellent Author UX:&lt;/strong&gt; Editors see only the fields that are directly relevant to what they are creating. No noise, no confusion, and the choice happens up front in a single step.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Precise Validation:&lt;/strong&gt; You can make &lt;strong&gt;FormId &lt;/strong&gt;strictly required on Gated Videos and &lt;strong&gt;VideoFile &lt;/strong&gt;required on both, with no overlaps&amp;mdash;and no &quot;empty wrapper&quot; edge case to guard against.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Evolvable Schemas:&lt;/strong&gt; If you need to add an &quot;External Tracker ID&quot; to Gated Videos later, you add it to the Gated type. The Standard Video type remains completely untouched, preventing regressions.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Flat GraphQL queries:&lt;/strong&gt; Each type resolves directly, with no nested content-area fragment to traverse.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Minor Code Duplication:&lt;/strong&gt; In some architectures, this might mean registering two types instead of one (though Optimizely&#39;s SDK makes this incredibly clean, as shown below).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Implementing Option 3 Cleanly with the Content JS SDK&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Implementation Disclaimer:&lt;/strong&gt; &lt;em&gt;The following code snippets are illustrative implementations demonstrating the structure and APIs of the &lt;/em&gt;&lt;strong&gt;@optimizely/cms-sdk&lt;/strong&gt;&lt;em&gt;. Depending on your active SDK version, environment settings, and target packages, always verify the exact properties, imports, and field parameters in your sandbox prior to deploying content types to production.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;1. Define a Shared Contract in Code&lt;/h3&gt;
&lt;p&gt;The SDK&#39;s &lt;strong&gt;contract()&lt;/strong&gt; function lets you declare the shared fields once. It keeps your codebase DRY, but merges the properties directly into each schema under the hood so authors get a flat, non-nested editor form:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;// src/contracts/VideoBaseContract.ts
import { contract } from &#39;@optimizely/cms-sdk&#39;;

export const VideoBaseContract = contract({
  key: &#39;VideoBase&#39;,
  properties: {
    videoFile: { type: &#39;contentReference&#39;, allowedTypes: [&#39;_video&#39;], required: true },
    posterImage: { type: &#39;contentReference&#39;, allowedTypes: [&#39;_image&#39;], required: true },
    title: { type: &#39;string&#39;, required: true },
  },
});&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2. Extend the Contract in Your Content Types&lt;/h3&gt;
&lt;p&gt;Both content types simply extend our base contract:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;// src/components/StandardVideo.ts
export const StandardVideoContentType = contentType({
  key: &#39;StandardVideo&#39;,
  baseType: &#39;_component&#39;,
  extends: VideoBaseContract, // &amp;lt;-- Merges shared fields
  properties: {
    autoplay: { type: &#39;boolean&#39;, group: &#39;playerOptions&#39; },
    loop: { type: &#39;boolean&#39;, group: &#39;playerOptions&#39; },
    showControls: { type: &#39;boolean&#39;, group: &#39;playerOptions&#39; },
  },
});

// src/components/GatedVideo.ts
export const GatedVideoContentType = contentType({
  key: &#39;GatedVideo&#39;,
  baseType: &#39;_component&#39;,
  extends: VideoBaseContract, // &amp;lt;-- Merges shared fields
  properties: {
    formId: { type: &#39;string&#39;, group: &#39;leadCapture&#39; },
    gateTimestamp: { type: &#39;integer&#39;, group: &#39;leadCapture&#39; },
  },
});&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3. Resolve to a Single React Component&lt;/h3&gt;
&lt;p&gt;Here is the developer&#39;s favorite trick: even though we have two schemas in the CMS, &lt;strong&gt;we can map them both to a single React component.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In our registry setup, we point both type keys to our &lt;strong&gt;&amp;lt;VideoItem /&amp;gt;&lt;/strong&gt; component. The component inspects &lt;strong&gt;__typename&lt;/strong&gt; (always supplied by Optimizely Graph) to decide how to render:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;// src/app/layout.tsx &amp;mdash; Map both keys to the same component
initReactComponentRegistry({
  resolver: {
    StandardVideo: VideoItem,
    GatedVideo:    VideoItem,
  },
});

// src/components/VideoItem.tsx &amp;mdash; One entry, two layouts
export default function VideoItem({ content }: Props) {
  if (content.__typename === &#39;GatedVideo&#39;) {
    return &amp;lt;GatedVideoView content={content} /&amp;gt;; // Typed as GatedVideoProps
  }
  return &amp;lt;StandardVideoView content={content} /&amp;gt;; // Typed as StandardVideoProps
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Conclusion: Choosing What&#39;s Right for Your Team&lt;/h2&gt;
&lt;p&gt;There is rarely a single &quot;correct&quot; answer in software architecture&amp;mdash;only choices and their consequences.&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;If your team is very small, your content needs are highly fluid, and you prioritize a rapid setup above all else, &lt;strong&gt;Option 1 (The Consolidated Block)&lt;/strong&gt; might serve you well.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If your design system relies heavily on composable, modular sub-assemblies and your editors are comfortable with multi-step nesting, &lt;strong&gt;Option 2 (The Nested Wrapper)&lt;/strong&gt; offers high structural reuse.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you prioritize editor productivity, robust validation, and clean GraphQL queries, &lt;strong&gt;Option 3 (The Split-Type Pattern)&lt;/strong&gt; combined with the SDK&#39;s single component routing gives you the best of both worlds: flat schemas for authors, and type-safe, DRY code for developers.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By laying these options on the table, your team can align on a modeling philosophy that matches your technical maturity and authoring workflows.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;Resources:&amp;nbsp;&lt;/em&gt;&lt;a href=&quot;https://github.com/episerver/content-js-sdk/tree/main/docs&quot;&gt;&lt;em&gt;Content JS SDK documentation&lt;/em&gt;&lt;/a&gt;&lt;em&gt; &amp;middot; &lt;/em&gt;&lt;a href=&quot;https://docs.developers.optimizely.com/content-management-system/v1.0.0-CMS-SaaS/docs/content-modeling-saas&quot;&gt;&lt;em&gt;Optimizely content modeling principles&lt;/em&gt;&lt;/a&gt;&lt;em&gt; &amp;middot; &lt;/em&gt;&lt;a href=&quot;https://docs.developers.optimizely.com/content-management-system/v1.0.0-CMS-SaaS/docs/content-base-types-saas&quot;&gt;&lt;em&gt;Content base types&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;</id><updated>2026-07-31T03:15:37.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Parallel Development in Optimizely CMS SaaS: Shifting to a Schema Migration Mindset</title><link href="https://world.optimizely.com/blogs/vipin-banka--learnings--insights/dates/2026/7/parallel-development-in-optimizely-cms-saas-shifting-to-a-schema-migration-mindset/" /><id>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Part 3 of the Parallel Development series.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The branch-scoped push script from &lt;a href=&quot;/link/60050e9906014446bb28143b0ca1b0c2.aspx&quot;&gt;Part 1&lt;/a&gt; works. Run it on a feature branch where you&#39;ve touched a handful of independent content types and it does exactly what it promises &amp;mdash; pushes only your changes, leaves your teammate&#39;s work untouched, no &lt;strong&gt;--force&lt;/strong&gt; required.&lt;/p&gt;
&lt;p&gt;For most of your day-to-day work, that&#39;s enough. The majority of content type changes are leaf changes: you added a field to a page, you tweaked a block, you updated a template. The script handles all of those cleanly.&lt;/p&gt;
&lt;p&gt;But as soon as you start moving past basic, isolated components and build toward a real content model&amp;mdash;with custom contracts, shared property groups, and complex validation rules&amp;mdash;the landscape changes. The script still works, but running selective push against a full, production-grade schema teaches you a few things worth mapping out before you hit them cold. None of this is a flaw in the selective push approach. These dependency cases exist in any code-first workflow with the CLI, whether you push selectively or all at once. What changes is whether you walk into them knowing what&#39;s happening, or whether you spend an afternoon confused at an API rejection.&lt;/p&gt;
&lt;p&gt;This post maps the five hidden dependency cases, shows what each looks like in code, and proposes a repo structure plus a reviewable push workflow that makes them more manageable. These are starting points and open questions &amp;mdash; not a finished solution. We&#39;d genuinely like to hear how other teams are thinking about this.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;First: &quot;Why not just always full push?&quot;&lt;/h2&gt;
&lt;p&gt;It&#39;s the fair question, and worth answering before anything else &amp;mdash; because if full push were always fine, none of this would matter.&lt;/p&gt;
&lt;p&gt;Full push is authoritative and safe &lt;em&gt;against a clean, canonical branch&lt;/em&gt;. That&#39;s exactly why it&#39;s the right tool for your CI/CD pipeline pushing from &lt;strong&gt;main&lt;/strong&gt;. But on a &lt;strong&gt;shared development instance where several people have unmerged work&lt;/strong&gt;, full push is the opposite of safe: it syncs your &lt;em&gt;entire&lt;/em&gt; local model, including stale or half-built types, over whatever your teammates have pushed. That&#39;s the collision Part 1 set out to solve.&lt;/p&gt;
&lt;p&gt;And when the CLI suggests &lt;strong&gt;--force&lt;/strong&gt;? That&#39;s not a fix. It&#39;s the CLI telling you the remote already has changes you&#39;re about to overwrite. The correct response is to &lt;strong&gt;sync with your team&lt;/strong&gt;, not to force through. Force is how one developer silently clobbers another&#39;s content types.&lt;/p&gt;
&lt;p&gt;So the real trade-off isn&#39;t &quot;selective push (hard) vs full push (easy).&quot; It&#39;s:&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Independent leaf changes (the 80%)&lt;/strong&gt; &amp;mdash; selective push is fully automatic and strictly better. No contest.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The five dependency cases&lt;/strong&gt; &amp;mdash; selective push &lt;em&gt;still&lt;/em&gt; beats full push on a shared instance, because a scoped-but-complete push includes what&#39;s needed and touches nothing else. The catch is you have to know what &quot;complete&quot; means. That&#39;s what this article tries to make knowable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Key renames and deletions&lt;/strong&gt; &amp;mdash; these genuinely are &quot;stop and coordinate&quot; moments. The honest value here isn&#39;t a clever script; it&#39;s &lt;em&gt;recognising&lt;/em&gt; you&#39;re in one, so you sync with the team and run a deliberate full push instead of forcing blindly.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&#39;s the frame for everything below. This isn&#39;t about avoiding full push forever &amp;mdash; it&#39;s about knowing which situation you&#39;re in.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The Five Hidden Dependencies&lt;/h2&gt;
&lt;p&gt;In Optimizely CMS SaaS, we&#39;ve identified five distinct cases where a change to one file creates a dependency that requires other files to be pushed in the same payload. Before we look at how to handle them, here is the quick map of the territory:&lt;/p&gt;
&lt;ol class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Contract changes&lt;/strong&gt; &amp;mdash; Modifying an interface pulls in all content types implementing it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Property group changes&lt;/strong&gt; &amp;mdash; Renaming or removing a group key affects every content type assigned to it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Component embeds&lt;/strong&gt; &amp;mdash; Schema shifts on a component affect any content type using them as typed properties.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Validation constraints&lt;/strong&gt; &amp;mdash; Changes to referenced type keys break &lt;strong&gt;allowedTypes&lt;/strong&gt; or &lt;strong&gt;restrictedTypes&lt;/strong&gt; in other files.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Key renames&lt;/strong&gt; &amp;mdash; A key value change is a destructive delete-and-create that orphans content and breaks external references.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For each case, the &quot;how to handle it&quot; notes reflect our own thinking and what seems reasonable &amp;mdash; not tested playbooks. Your project&#39;s structure and conventions will change what actually works.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Case 1 &amp;mdash; Contract change &amp;rarr; all implementing types&lt;/h2&gt;
&lt;p&gt;Contracts define a shared set of properties that multiple content types implement. Add a field to a contract, and every implementing type has a stale resolved schema on the server &amp;mdash; because that field is now part of their schema too, whether they declared it explicitly or not.&lt;/p&gt;
&lt;p&gt;This is transitive. If Contract A is implemented by Contract B, and Contract B is implemented by five page types, a change to Contract A means all six need to be in the same push.&lt;/p&gt;
&lt;h3&gt;Why isolated contract pushes fail&lt;/h3&gt;
&lt;p&gt;The temptation is to slice pushes into layers &amp;mdash; a &quot;foundation&quot; config that covers contracts, a &quot;types&quot; config that covers pages and blocks. Different concerns, different cadence. The logic is sound.&lt;/p&gt;
&lt;p&gt;Then you try it on a live instance where types already implement the contract:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;# Fails if any content type already implements the contract on the target instance
npx @optimizely/cms-cli@latest push --config optimizely.config.foundation.mjs&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The CLI errors out. The API rejects the payload.&lt;/p&gt;
&lt;p&gt;Based on observed behaviour, when a contract changes on the server, the CMS appears to immediately recompile the fully resolved schema of every implementing type. If those types aren&#39;t in the same push payload, the API rejects the whole operation rather than leave the instance in a half-compiled state. This is the right call &amp;mdash; it&#39;s protecting you. But it means contracts and their implementers can&#39;t travel separately once the contract is applied.&lt;/p&gt;
&lt;p&gt;&#128214; &lt;a href=&quot;https://docs.developers.optimizely.com/content-management-system/v1.0.0-CMS-SaaS/docs/create-contracts&quot;&gt;Create contracts&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note on code examples:&lt;/strong&gt; The JSON and TypeScript shapes shown throughout this article are illustrative of the concepts. Always check the current CLI &lt;strong&gt;--help&lt;/strong&gt; output and the &lt;a href=&quot;https://docs.developers.optimizely.com/content-management-system/v1.0.0-CMS-SaaS/docs/manage-content-types-using-the-rest-api&quot;&gt;REST API reference&lt;/a&gt; for exact syntax on your version.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;// Contract &amp;mdash; POST https://api.cms.optimizely.com/v1/contenttypes
{
  &quot;key&quot;: &quot;categorizable&quot;,
  &quot;displayName&quot;: &quot;Categorizable&quot;,
  &quot;properties&quot;: {
    &quot;category&quot;: { &quot;type&quot;: &quot;string&quot;, &quot;displayName&quot;: &quot;Category&quot; },
    &quot;tags&quot;:     { &quot;type&quot;: &quot;array&quot;,  &quot;displayName&quot;: &quot;Tags&quot;, &quot;items&quot;: { &quot;type&quot;: &quot;string&quot; } }
  }
}

// Content type implementing the contract
// Adding a field to &#39;categorizable&#39; above means this type must be in the same push
{
  &quot;key&quot;: &quot;articlePage&quot;,
  &quot;baseType&quot;: &quot;_page&quot;,
  &quot;contracts&quot;: [&quot;categorizable&quot;],
  &quot;properties&quot;: {
    &quot;title&quot;: { &quot;type&quot;: &quot;string&quot;,   &quot;displayName&quot;: &quot;Title&quot; },
    &quot;body&quot;:  { &quot;type&quot;: &quot;richText&quot;, &quot;displayName&quot;: &quot;Body&quot; }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;One possible approach:&lt;/strong&gt; Scan all content-type files for any import or string reference to the changed contract&#39;s file name, and include matches in the push set. Whether that works reliably depends on how consistently your codebase names and imports contracts &amp;mdash; a loosely structured project may need a more explicit dependency map. We haven&#39;t found a clean universal answer here.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;Case 2 &amp;mdash; Property group change &amp;rarr; all types referencing that group&lt;/h2&gt;
&lt;p&gt;Property groups are defined in &lt;strong&gt;optimizely.config.mjs&lt;/strong&gt; via &lt;strong&gt;buildConfig&lt;/strong&gt; and referenced by &lt;strong&gt;key&lt;/strong&gt; inside property definitions. Change or remove that key, and any type that assigns properties to it fails validation.&lt;/p&gt;
&lt;p&gt;&#128214; &lt;a href=&quot;https://docs.developers.optimizely.com/content-management-system/v1.0.0-CMS-SaaS/docs/property-groups-saas&quot;&gt;Property groups&lt;/a&gt;&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;// optimizely.config.mjs
import { buildConfig } from &#39;@optimizely/cms-sdk&#39;;

export default buildConfig({
  components: [&#39;./src/content-model/**/*.tsx&#39;],
  propertyGroups: [
    { key: &#39;seo&#39;, displayName: &#39;SEO Settings&#39;, sortOrder: 10 }
    // Rename &#39;seo&#39; here &amp;rarr; every type referencing this key must be re-pushed
  ]
});&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;// PATCH https://api.cms.optimizely.com/v1/contenttypes/articlePage
{
  &quot;key&quot;: &quot;articlePage&quot;,
  &quot;baseType&quot;: &quot;_page&quot;,
  &quot;properties&quot;: {
    &quot;metaTitle&quot;:       { &quot;type&quot;: &quot;string&quot;, &quot;displayName&quot;: &quot;Meta Title&quot;,       &quot;group&quot;: &quot;seo&quot; },
    &quot;metaDescription&quot;: { &quot;type&quot;: &quot;string&quot;, &quot;displayName&quot;: &quot;Meta Description&quot;, &quot;group&quot;: &quot;seo&quot; }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rename the group key without re-pushing &lt;strong&gt;articlePage&lt;/strong&gt; and its &lt;strong&gt;metaTitle&lt;/strong&gt; / &lt;strong&gt;metaDescription&lt;/strong&gt; properties now point to a group that doesn&#39;t exist on the instance.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;One possible approach:&lt;/strong&gt; Diff &lt;strong&gt;optimizely.config.mjs&lt;/strong&gt;, extract changed group keys, and search content-type files for those strings. Since the config change is usually deliberate and visible in review, this might be something you handle at review time rather than automating &amp;mdash; though what&#39;s right depends entirely on your team&#39;s workflow and how often these keys change.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;Case 3 &amp;mdash; Component used as a typed property &amp;rarr; the embedding type&lt;/h2&gt;
&lt;p&gt;A &lt;strong&gt;_component&lt;/strong&gt; content type can be declared as a fixed typed property on another content type &amp;mdash; not just dropped into a content area at runtime, but &lt;em&gt;declared&lt;/em&gt; in the schema as a property of that specific type. Optimizely Graph registers both &lt;strong&gt;TeaserBlock&lt;/strong&gt; (standalone) and &lt;strong&gt;TeaserBlockProperty&lt;/strong&gt; (when embedded as a schema property) to support this.&lt;/p&gt;
&lt;p&gt;When the component&#39;s schema changes, the type that embeds it is resolving a shape that has shifted.&lt;/p&gt;
&lt;p&gt;&#128214; &lt;a href=&quot;https://docs.developers.optimizely.com/content-management-system/v1.0.0-CMS-SaaS/docs/content-modeling-saas-graph&quot;&gt;Content modeling &amp;mdash; content properties&lt;/a&gt; &amp;middot; &lt;a href=&quot;https://docs.developers.optimizely.com/content-management-system/v1.0.0-CMS-SaaS/docs/content-types-saas&quot;&gt;Define content types&lt;/a&gt;&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;// The component &amp;mdash; POST https://api.cms.optimizely.com/v1/contenttypes
{
  &quot;key&quot;: &quot;teaserBlock&quot;,
  &quot;baseType&quot;: &quot;_component&quot;,
  &quot;properties&quot;: {
    &quot;heading&quot;: { &quot;type&quot;: &quot;string&quot;,           &quot;displayName&quot;: &quot;Heading&quot; },
    &quot;image&quot;:   { &quot;type&quot;: &quot;contentReference&quot;, &quot;displayName&quot;: &quot;Image&quot; }
    // Add a new required field here &amp;rarr; homePage below must be in the same push
  }
}

// A page declaring the component as a typed property
{
  &quot;key&quot;: &quot;homePage&quot;,
  &quot;baseType&quot;: &quot;_page&quot;,
  &quot;properties&quot;: {
    &quot;hero&quot;: { &quot;type&quot;: &quot;content&quot;, &quot;displayName&quot;: &quot;Hero Teaser&quot;, &quot;contentType&quot;: &quot;teaserBlock&quot; }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;One possible approach:&lt;/strong&gt; Scan content-type files for references to the changed component&#39;s name or key. The tricky part is distinguishing a typed schema embed from a regular rendering import &amp;mdash; a flat folder makes this ambiguous, which is part of why the folder structure below is worth considering.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;Case 4 &amp;mdash; allowedTypes / restrictedTypes references &amp;rarr; the referencing type&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;allowedTypes&lt;/strong&gt; and &lt;strong&gt;restrictedTypes&lt;/strong&gt; are validation constraints on &lt;strong&gt;content&lt;/strong&gt; and &lt;strong&gt;contentReference&lt;/strong&gt; properties. They name other types &lt;strong&gt;by their key&lt;/strong&gt;. That reference is invisible to a git diff.&lt;/p&gt;
&lt;p&gt;&#128214; &lt;a href=&quot;https://docs.developers.optimizely.com/content-management-system/v1.0.0-CMS-SaaS/docs/content-property&quot;&gt;Content property &amp;mdash; allowedTypes&lt;/a&gt; &amp;middot; &lt;a href=&quot;https://docs.developers.optimizely.com/content-management-system/v1.0.0-CMS-SaaS/docs/content-types-saas&quot;&gt;Define content types&lt;/a&gt;&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;// PATCH https://api.cms.optimizely.com/v1/contenttypes/landingPage
{
  &quot;key&quot;: &quot;landingPage&quot;,
  &quot;baseType&quot;: &quot;_page&quot;,
  &quot;properties&quot;: {
    &quot;relatedContent&quot;: {
      &quot;type&quot;: &quot;contentReference&quot;,
      &quot;displayName&quot;: &quot;Related Content&quot;,
      &quot;allowedTypes&quot;: [&quot;articlePage&quot;, &quot;blogPage&quot;]
    }
  }
}

// If &#39;blogPage&#39; is renamed to &#39;blogPostPage&#39; &amp;mdash;
// git diff shows BlogPage.tsx changed
// git diff does NOT show landingPage.tsx changed
// but landingPage now references a key that doesn&#39;t exist&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Adding a &lt;em&gt;new&lt;/em&gt; type to &lt;strong&gt;allowedTypes&lt;/strong&gt; is completely safe &amp;mdash; it&#39;s in your diff and selective push handles it automatically.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;One possible approach:&lt;/strong&gt; A pre-push step that reads all &lt;strong&gt;allowedTypes&lt;/strong&gt; / &lt;strong&gt;restrictedTypes&lt;/strong&gt; arrays and verifies every key they name still exists in the codebase could act as an early warning. Whether that&#39;s worth building depends on how often your model changes and how much a broken shared environment costs your team.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;Case 5 &amp;mdash; Key rename: unavoidable sometimes, never trivial&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Design your keys well upfront and you likely won&#39;t face this. But if you do, know exactly what happens.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The &lt;strong&gt;key&lt;/strong&gt; of a content type, contract, or property is its permanent identity on the CMS instance. There is no rename operation &amp;mdash; what actually happens is a &lt;strong&gt;delete of the old + create of the new&lt;/strong&gt;. The CMS treats them as two completely different entities.&lt;/p&gt;
&lt;p&gt;Consequences:&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;All content items created under the old key are orphaned&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Every type that names the old key &amp;mdash; in &lt;strong&gt;allowedTypes&lt;/strong&gt;, &lt;strong&gt;restrictedTypes&lt;/strong&gt;, typed &lt;strong&gt;contentType&lt;/strong&gt; properties, &lt;strong&gt;contracts&lt;/strong&gt; arrays &amp;mdash; is now broken&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Git diff shows only the file where the key string changed, nothing about files that reference it elsewhere&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&#128214; &lt;a href=&quot;https://docs.developers.optimizely.com/content-management-system/v1.0.0-CMS-SaaS/docs/manage-content-types-using-the-rest-api&quot;&gt;Manage content types &amp;mdash; key field&lt;/a&gt;&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;// BEFORE: &#39;blogPage&#39; is the live key
{ &quot;key&quot;: &quot;blogPage&quot;, &quot;baseType&quot;: &quot;_page&quot; }
{ &quot;allowedTypes&quot;: [&quot;articlePage&quot;, &quot;blogPage&quot;] }   // another type, referencing it

// AFTER: key renamed to &#39;blogPostPage&#39;
{ &quot;key&quot;: &quot;blogPostPage&quot;, &quot;baseType&quot;: &quot;_page&quot; }     // BlogPage.tsx &amp;mdash; in your diff
{ &quot;allowedTypes&quot;: [&quot;articlePage&quot;, &quot;blogPage&quot;] }   // LandingPage.tsx &amp;mdash; NOT in your diff, now broken&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;The realistic minimum when a rename is unavoidable:&lt;/strong&gt;&lt;/p&gt;
&lt;ol class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;Search the full codebase for the old key string (&lt;strong&gt;grep -r &quot;blogPage&quot; src/&lt;/strong&gt; is the starting point)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update every file that references it in the same branch&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run a &lt;strong&gt;coordinated full push&lt;/strong&gt; &amp;mdash; not a selective push&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Plan content migration separately &amp;mdash; existing content under the old key is not auto-migrated&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;displayName&lt;/strong&gt; is free to rename at any time. &lt;strong&gt;key&lt;/strong&gt; is permanent the moment it lands on an instance.&lt;/p&gt;
&lt;h3&gt;One direction worth exploring: detect the rename at diff time&lt;/h3&gt;
&lt;p&gt;Git has the information you need &amp;mdash; you can pull the &lt;em&gt;old&lt;/em&gt; version of any changed file and compare key values:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;# Get the old content of a file as it was on the base branch
git show origin/main:src/content-model/pages/BlogPage.tsx&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For each modified content-type file: extract the old &lt;strong&gt;key&lt;/strong&gt; and the new &lt;strong&gt;key&lt;/strong&gt;. If they differ, search the codebase for the old key and surface every file that still references it. That turns a silent, easy-to-miss breakage into a loud early warning. You still need the coordinated full push &amp;mdash; but at least you know why before you push, rather than after.&lt;/p&gt;
&lt;p&gt;We haven&#39;t battle-tested this in a complex repo. It&#39;s a direction that seems worth exploring, not a recommendation.&lt;/p&gt;
&lt;h4&gt;The Safe Alternative: The Editorial Deprecation Pattern&lt;/h4&gt;
&lt;p&gt;Instead of performing a destructive key rename, the industry-standard best practice is to &lt;strong&gt;deprecate&lt;/strong&gt; the old field and introduce the new one alongside it. While CMS SaaS doesn&#39;t have a native &quot;deprecated&quot; JSON flag in the schema, you can enforce this through an &lt;strong&gt;editorial convention&lt;/strong&gt; in your code:&lt;/p&gt;
&lt;ol class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Remove constraints:&lt;/strong&gt; Set &lt;strong&gt;&quot;required&quot;: false&lt;/strong&gt; on the old field so editors are never blocked.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Warn editors in the UI:&lt;/strong&gt; Update its &lt;strong&gt;displayName&lt;/strong&gt; to include &lt;strong&gt;(Obsolete)&lt;/strong&gt; and use the &lt;strong&gt;description&lt;/strong&gt; to point to the new property.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Warn developers in the IDE:&lt;/strong&gt; Add a JSDoc &lt;strong&gt;@deprecated&lt;/strong&gt; comment to the TypeScript interface.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;// src/content-model/components/TeaserBlock.tsx

export interface TeaserBlockType extends ContentType {
  heading: string;
  /** 
   * @deprecated Use &#39;heroImage&#39; instead. This field is scheduled for removal in Q4.
   */
  imageLink?: string; // Suffix with ? to make it optional in TS
}

export const TeaserBlockSchema = {
  key: &#39;teaserBlock&#39;,
  baseType: &#39;_component&#39;,
  displayName: &#39;Teaser Block&#39;,
  properties: {
    heading: { type: &#39;string&#39;, displayName: &#39;Heading&#39;, required: true },
    imageLink: {
      type: &#39;string&#39;,
      displayName: &#39;Image Link (Obsolete)&#39;,
      description: &#39;Deprecated. Use the Hero Image property instead.&#39;,
      required: false, // Must be optional to prevent editor lock-out
    },
  },
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This keeps the old schema active, preserves historical data on production, and gives both developers and editors loud warnings. Only after several release cycles (when audit logs confirm no content remains in the old field) do you coordinate with your team to delete the key.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Our suggested approach:&lt;/strong&gt; Treat renames as deliberate, planned events requiring a full codebase audit before any push. The git-blob detection above is a reasonable middle ground &amp;mdash; cheap to build, fails loudly. But no script makes a key rename safe on its own.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;The Repo Structure That Makes All of This More Tractable&lt;/h2&gt;
&lt;p&gt;Every detection approach above &amp;mdash; scanning for contract references, extracting group keys, comparing old and new key values &amp;mdash; relies on being able to tell &lt;em&gt;what kind of thing&lt;/em&gt; a changed file is from where it lives. In a flat folder where contracts, pages, blocks, and templates all coexist, that falls apart immediately.&lt;/p&gt;
&lt;p&gt;A deliberate folder structure is what makes any of this tractable. It won&#39;t solve the dependency problem on its own, but it gives your scripts &amp;mdash; and your reviewers &amp;mdash; something to reason from.&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code&gt;src/
└── content-model/           # Everything the CLI pushes as schema
    ├── contracts/             # Shared interfaces &amp;mdash; Case 1 blast radius
    │   ├── Categorizable.ts   # category + tags fields shared across types
    │   └── IHeroImage.ts      # heroImage + heroAlt fields
    ├── pages/                 # _page base type
    │   ├── ArticlePage.tsx
    │   ├── LandingPage.tsx
    │   └── BlogPage.tsx
    ├── components/            # _component base type (blocks AND elements)
    │   ├── TeaserBlock.tsx
    │   ├── HeroBlock.tsx
    │   ├── HeadingElement.tsx # an &quot;element&quot; is just a component with restricted config
    │   └── ParagraphElement.tsx
    ├── experiences/           # _experience base type
    │   └── BlankExperience.tsx
    └── sections/              # _section base type
        └── BlankSection.tsx

src/
└── templates/               # Display templates &amp;mdash; decoupled, own push lane
    ├── ArticlePage.style.ts   # Wide / Narrow display variants for ArticlePage
    ├── TeaserBlock.style.ts   # Card / Banner display variants for TeaserBlock
    └── section-grid.style.ts  # 1-col / 2-col / 3-col section layout options&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A note on &lt;strong&gt;elements&lt;/strong&gt;: in the JavaScript SDK, an element is not a separate base type &amp;mdash; it&#39;s a &lt;strong&gt;_component&lt;/strong&gt; with restricted configuration (it&#39;s meant to be used as a Visual Builder building block rather than a standalone block). Because it&#39;s still a component, it lives in the same folder and behaves identically for push-dependency purposes. Group by base type, not by editorial role.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Property groups&lt;/strong&gt; don&#39;t get their own files either &amp;mdash; they live in &lt;strong&gt;optimizely.config.mjs&lt;/strong&gt; as the propertyGroups array. If your config grows large you can split it into an imported module, but the source of truth stays the config file.&lt;/p&gt;
&lt;h3&gt;What templates actually are&lt;/h3&gt;
&lt;p&gt;A template file registers display variations that editors can apply to content nodes in Visual Builder. They are pure style/visual configuration &amp;mdash; they carry no content schema. This is why they can be pushed on their own lane without any dependency scanning.&lt;/p&gt;
&lt;p&gt;A display template applies to a content type (or base type, or structural node) and exposes editor-selectable settings:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;// src/templates/TeaserBlock.style.ts
// POST https://api.cms.optimizely.com/v1/displaytemplates

export const teaserBlockCardStyle = {
  key: &#39;TeaserBlockCard&#39;,
  displayName: &#39;Card&#39;,
  contentType: &#39;teaserBlock&#39;,     // applies to this specific content type
  settings: {
    ImagePosition: {
      displayName: &#39;Image Position&#39;,
      editor: &#39;select&#39;,
      choices: {
        Left:  { displayName: &#39;Left&#39;,  sortOrder: 1 },
        Right: { displayName: &#39;Right&#39;, sortOrder: 2 },
        Top:   { displayName: &#39;Top&#39;,   sortOrder: 3 }
      }
    }
  }
};&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;// src/templates/section-grid.style.ts
// A structural node template &amp;mdash; applies to all sections

export const sectionGridStyle = {
  key: &#39;SectionGrid&#39;,
  displayName: &#39;Grid&#39;,
  nodeType: &#39;section&#39;,            // applies to all section nodes
  settings: {
    Columns: {
      displayName: &#39;Columns&#39;,
      editor: &#39;select&#39;,
      choices: {
        One:   { displayName: &#39;1 column&#39;,  sortOrder: 1 },
        Two:   { displayName: &#39;2 columns&#39;, sortOrder: 2 },
        Three: { displayName: &#39;3 columns&#39;, sortOrder: 3 }
      }
    }
  }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&#128214; &lt;a href=&quot;https://docs.developers.optimizely.com/content-management-system/v1.0.0-CMS-SaaS/docs/configure-visual-builder&quot;&gt;Configure Visual Builder&lt;/a&gt; &amp;middot; &lt;a href=&quot;https://docs.developers.optimizely.com/content-management-system/v1.0.0-CMS-SaaS/docs/work-with-styles&quot;&gt;Manage styles&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;The folder pays off in the script&lt;/h3&gt;
&lt;p&gt;With this structure, a script can classify a changed file from its path alone:&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;A file under &lt;strong&gt;content-model/contracts/&lt;/strong&gt; &amp;rarr; run the Case 1 scan&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A file under &lt;strong&gt;content-model/pages/&lt;/strong&gt; or &lt;strong&gt;content-model/components/&lt;/strong&gt; &amp;rarr; run the Case 3 scan&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;optimizely.config.mjs&lt;/strong&gt; changed &amp;rarr; run the Case 2 check&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A file under &lt;strong&gt;templates/&lt;/strong&gt; &amp;rarr; push on the templates lane, no schema scan needed&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It also opens the door to something more responsive: a file watcher during development that flags dependency-affecting changes the moment you save, before you ever run a push. We haven&#39;t built that &amp;mdash; it&#39;s a direction the structure makes possible.&lt;/p&gt;
&lt;p&gt;The honest caveat: no folder convention survives a team that doesn&#39;t follow it. The structure only works if it&#39;s agreed, documented, and enforced.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Shifting Our Mindset: Content Modeling as Schema Migration&lt;/h2&gt;
&lt;p&gt;When you build on a headless, code-first content platform, your content type files are not just React rendering files. They represent your &lt;strong&gt;live database schema, your API contract, and your editorial UI layout all at once.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In traditional software engineering, we treat database schemas with extreme discipline. You would never let a CI/CD build run a migration that drops database columns containing live customer data. You write versioned migrations, test them locally, and deploy them sequentially.&lt;/p&gt;
&lt;p&gt;Content modeling in CMS SaaS requires the exact same engineering discipline.&lt;/p&gt;
&lt;p&gt;This realization completely changes how we govern our code-first pipelines and why selective pushes exist:&lt;/p&gt;
&lt;ol class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Selective push is your local iteration tool.&lt;/strong&gt; It is how developers move quickly on a shared sandbox without overwriting other unmerged work.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The script is your diagnostic blueprint.&lt;/strong&gt; When a build fails or conflicts occur, the branch-diff script is what you run to instantly map out the exact footprint of your changes and see which contracts or components are dragging dependents in.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;CI/CD is your schema safety gate.&lt;/strong&gt; It must run strictly, never forcing, and failing loudly if anything is out of sync.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let&amp;rsquo;s look at how to structure your CI/CD pipeline to act as this hard firewall.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;CI/CD Guardrails: The &quot;Fail Loudly, Never Force&quot; Principle&lt;/h2&gt;
&lt;p&gt;In lower environments (like individual developer sandboxes), selective pushes and the propose-review-push workflow provide rapid feedback loops. But as your code travels up to shared environments, the governance must tighten.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; border-width: 1px; border-style: solid;&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Environment&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Purpose&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Push Strategy&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;&lt;strong&gt;CLI Flags&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Local Sandbox&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;Individual dev space&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;Scoped / Selective push&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;Normal&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Shared QA / Stage&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;Team integration &amp;amp; content entry&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;Scoped or Full Push&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Strictly no --force&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Production&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;Live consumer experience&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;Authoritative Full Push&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Strictly no --force&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;Enforcing the Safety Gate&lt;/h3&gt;
&lt;p&gt;The &lt;strong&gt;@optimizely/cms-cli&lt;/strong&gt; is designed to fail loudly. If there is a schema conflict &amp;mdash; such as a missing implementing type for a modified contract, or an invalid group key reference &amp;mdash; the command exits with a non-zero exit code (typically &lt;strong&gt;1&lt;/strong&gt;).&lt;/p&gt;
&lt;p&gt;Any standard CI/CD engine (GitHub Actions, GitLab CI, Azure DevOps) will intercept this non-zero exit code and immediately fail the build step.&lt;/p&gt;
&lt;p&gt;By running your staging and production pipelines &lt;strong&gt;without the --force flag&lt;/strong&gt;, you ensure that if a developer checked in a breaking change without coordination, the build halts before a single production schema is modified or live data is orphaned.&lt;/p&gt;
&lt;h3&gt;Diagnostic Recovery Workflow&lt;/h3&gt;
&lt;p&gt;When the build fails, the team doesn&#39;t have to guess what broke. A developer runs the &lt;strong&gt;push-branch-changes.mjs&lt;/strong&gt; script locally comparing the release branch to the target base (e.g., &lt;strong&gt;origin/main&lt;/strong&gt;):&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;npm run cms:push:types release/q3 origin/main&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The script outputs the precise dependency tree of the changed files. The team can instantly pinpoint: &lt;em&gt;&quot;Ah! Someone updated the contract, which dragged in ArticlePage. But ArticlePage on Stage has custom fields that are still locked in another draft. That&#39;s our conflict.&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It turns a black-box build failure into an actionable, reviewable transaction map.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The Workflow: Propose, Review, Push&lt;/h2&gt;
&lt;p&gt;The script below doesn&#39;t push for you automatically. It detects, proposes, and hands you a reviewable config. You decide whether to push it.&lt;/p&gt;
&lt;p&gt;The loop:&lt;/p&gt;
&lt;ol class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;Run one command against your branch.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The script diffs your branch, classifies each changed file by its folder, and runs the relevant dependency scans for Cases 1&amp;ndash;3.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It &lt;strong&gt;writes a temporary config&lt;/strong&gt; listing the proposed push set &amp;mdash; your changed files plus every dependent it found &amp;mdash; and prints the list.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You review the list. Does it look right? Anything surprising, or anything missing you expected? For Cases 4&amp;ndash;5 it flags what needs a human decision.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you&#39;re satisfied, you run the push command it hands you.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This isn&#39;t overhead instead of a solution. It&#39;s a gate that makes a scoped push reviewable before it lands on a shared instance &amp;mdash; something a blind full push can&#39;t give you.&lt;/p&gt;
&lt;p&gt;Two commands, split by concern:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;// package.json
&quot;scripts&quot;: {
  &quot;cms:push:templates&quot;: &quot;npx @optimizely/cms-cli@latest push --config optimizely.config.templates.mjs&quot;,
  &quot;cms:push:types&quot;:     &quot;node push-branch-changes.mjs&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Templates lane &amp;mdash;&lt;/strong&gt; display templates and Visual Builder styles. Zero schema risk. No review needed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Types lane &amp;mdash;&lt;/strong&gt; content types and contracts together, via the propose-review-push script below.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;One Rough Starting Point for the Script&lt;/h2&gt;
&lt;p&gt;This extends the script from Part 1 to attempt dependency detection for Cases 1&amp;ndash;3. We&#39;re sharing it as a concept, not a drop-in solution. It makes simplifying assumptions &amp;mdash; consistent folder structure, filename-based detection, flat directory scanning &amp;mdash; that won&#39;t hold in every project. Treat it as something to adapt, not something to copy.&lt;/p&gt;
&lt;p&gt;Cases 4 and 5 (key renames and deletions) are not automatable in any reliable way. The script blocks on file deletions to at least surface the problem. Key value renames it cannot detect at all without the git-blob comparison described in Case 5.&lt;/p&gt;
&lt;p&gt;&#128214; &lt;a href=&quot;https://docs.developers.optimizely.com/content-management-system/v1.0.0-CMS-SaaS/docs&quot;&gt;JavaScript SDK docs&lt;/a&gt;&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;// push-branch-changes.mjs &amp;mdash; adapt to your folder structure
import { execSync } from &#39;child_process&#39;;
import fs from &#39;fs&#39;;
import path from &#39;path&#39;;

const baseBranch = process.argv[2] || &#39;origin/main&#39;;
const tempConfig = &#39;optimizely.config.temp.mjs&#39;;

// Match your actual folder structure. Note: elements live under components.
const PAGES_DIR      = &#39;src/content-model/pages&#39;;
const COMPONENTS_DIR = &#39;src/content-model/components&#39;;
const CONTRACTS_DIR  = &#39;src/content-model/contracts&#39;;
const ALL_TYPES_DIRS = [PAGES_DIR, COMPONENTS_DIR, CONTRACTS_DIR];

try {
  console.log(`Diffing against ${baseBranch}...`);

  const gitOutput = execSync(`git diff ${baseBranch} --name-only`, { encoding: &#39;utf8&#39; });
  const changedFiles = gitOutput
    .split(&#39;\n&#39;).map(f =&amp;gt; f.trim())
    .filter(f =&amp;gt; f.length &amp;gt; 0 &amp;amp;&amp;amp; fs.existsSync(f));

  if (changedFiles.length === 0) {
    console.log(&#39;No changes. Nothing to push.&#39;);
    process.exit(0);
  }

  // ── Block on deletions &amp;mdash; requires human audit before any push
  const deletedFiles = execSync(
    `git diff ${baseBranch} --name-only --diff-filter=D`, { encoding: &#39;utf8&#39; }
  ).split(&#39;\n&#39;).map(f =&amp;gt; f.trim()).filter(Boolean);

  const deletedTypes = deletedFiles.filter(f =&amp;gt; ALL_TYPES_DIRS.some(d =&amp;gt; f.startsWith(d)));
  if (deletedTypes.length &amp;gt; 0) {
    console.error(&#39;\n[BLOCKED] Deleted content type or contract files:&#39;);
    deletedTypes.forEach(f =&amp;gt; console.error(` - ${f}`));
    console.error(&#39;\nAudit all referencing files before running a coordinated full push.\n&#39;);
    process.exit(1);
  }

  const pushRegistry = new Set();
  changedFiles
    .filter(f =&amp;gt; ALL_TYPES_DIRS.some(d =&amp;gt; f.startsWith(d)))
    .forEach(f =&amp;gt; pushRegistry.add(f));

  // All content-model files &amp;mdash; used for scanning
  const allTypeFiles = ALL_TYPES_DIRS.flatMap(dir =&amp;gt;
    fs.existsSync(dir)
      ? fs.readdirSync(dir, { recursive: true })
          .filter(f =&amp;gt; f.endsWith(&#39;.tsx&#39;) || f.endsWith(&#39;.ts&#39;))
          .map(f =&amp;gt; path.join(dir, f))
      : []
  );

  // ── Case 1: Contract changes &amp;rarr; scan for implementing types
  const changedContracts = changedFiles.filter(f =&amp;gt; f.startsWith(CONTRACTS_DIR));
  if (changedContracts.length &amp;gt; 0) {
    console.log(&#39;\n[Case 1] Contract changes &amp;mdash; scanning for implementing types...&#39;);
    const names = changedContracts.map(f =&amp;gt; path.basename(f, path.extname(f)));
    expandDependents(allTypeFiles, names, pushRegistry, &#39;implements contract&#39;);
  }

  // ── Case 2: Property group key changes &amp;rarr; scan for referencing types
  const changedConfig = changedFiles.find(f =&amp;gt; f === &#39;optimizely.config.mjs&#39;);
  if (changedConfig) {
    const src = fs.readFileSync(changedConfig, &#39;utf8&#39;);
    const groupKeys = [...src.matchAll(/key:\s*[&#39;&quot;](\w[\w-]*)[&#39;&quot;]\s*,\s*displayName/g)].map(m =&amp;gt; m[1]);
    if (groupKeys.length &amp;gt; 0) {
      console.log(`\n[Case 2] Property group keys (${groupKeys.join(&#39;, &#39;)}) &amp;mdash; scanning referencing types...`);
      expandDependents(allTypeFiles, groupKeys, pushRegistry, &#39;references group key&#39;);
    }
  }

  // ── Case 3: Component/page changes &amp;rarr; scan for types that embed them
  const changedComponents = changedFiles.filter(f =&amp;gt;
    f.startsWith(COMPONENTS_DIR) || f.startsWith(PAGES_DIR)
  );
  if (changedComponents.length &amp;gt; 0) {
    const names = changedComponents.map(f =&amp;gt; path.basename(f, path.extname(f)));
    console.log(&#39;\n[Case 3] Component/page changes &amp;mdash; scanning for embedding types...&#39;);
    expandDependents(allTypeFiles, names, pushRegistry, &#39;embeds as typed property&#39;);
  }

  const finalPushList = Array.from(pushRegistry);
  if (finalPushList.length === 0) {
    console.log(&#39;No pushable schema changes found.&#39;);
    process.exit(0);
  }

  // ── Propose: write the temp config and print it for review
  console.log(`\nProposed push set (${finalPushList.length} files) &amp;mdash; review before confirming:`);
  finalPushList.forEach(f =&amp;gt; console.log(` - ${f}`));

  const filePaths = finalPushList.map(f =&amp;gt; `&#39;./${f}&#39;`).join(&#39;,\n    &#39;);
  fs.writeFileSync(tempConfig,
    `import { buildConfig } from &#39;@optimizely/cms-sdk&#39;;
export default buildConfig({ components: [\n    ${filePaths}\n  ] });
`, &#39;utf8&#39;);

  console.log(`\nWrote ${tempConfig}. Review it, then push with:`);
  console.log(`  npx @optimizely/cms-cli@latest push --config ${tempConfig}\n`);
  // The script stops here and hands you the command rather than pushing automatically.
  // If you&#39;re confident in the detection for your project, you can execSync the push instead.

} catch (err) {
  console.error(&#39;Failed:&#39;, err.message);
  if (fs.existsSync(tempConfig)) fs.unlinkSync(tempConfig);
  process.exit(1);
}

function expandDependents(allFiles, names, registry, reason) {
  for (const filePath of allFiles) {
    if (registry.has(filePath)) continue;
    const source = fs.readFileSync(filePath, &#39;utf8&#39;);
    const matched = names.find(name =&amp;gt; source.includes(name));
    if (matched) {
      console.log(` -&amp;gt; Including ${filePath} (${reason}: &#39;${matched}&#39;)`);
      registry.add(filePath);
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Known limitations worth being honest about:&lt;/strong&gt; String scanning produces false positives in loosely structured projects &amp;mdash; a rendering import looks identical to a schema embed reference. The property group regex is fragile if your config format differs. Key renames inside a still-present file are invisible to this script entirely. These aren&#39;t edge cases to dismiss &amp;mdash; they&#39;re reasons to test this against your actual project before leaning on it.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Forward Looking: Maturing to a Shared Package&lt;/h2&gt;
&lt;p&gt;For larger enterprise projects or teams handling multiple concurrent front-end applications, a final maturity step is to &lt;strong&gt;extract the content model layer into its own npm package&lt;/strong&gt; (such as &lt;strong&gt;@yourorg/content-model&lt;/strong&gt;).&lt;/p&gt;
&lt;p&gt;This moves your schema definitions into their own independent repository with strict versioning and ownership:&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Strict Review Gates:&lt;/strong&gt; The package can have its own &lt;strong&gt;package.json&lt;/strong&gt;, its own CI validation, and its own &lt;strong&gt;CODEOWNERS&lt;/strong&gt; file &amp;mdash; meaning no developer can alter a contract or change a schema key without explicit approval from a content architect.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Semantic Versioning:&lt;/strong&gt; Schema changes are published as versioned releases. A breaking key rename is mapped to a Major version bump, acting as a clear, standard signal to every consuming front-end application.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Isolate the Pipeline:&lt;/strong&gt; The CI/CD push logic lives with the package that defines the models. The client applications simply consume the published TypeScript interfaces.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While a separate package adds publishing overhead that might be overkill for smaller single-team projects, it represents the logical north star for teams managing Optimizely CMS SaaS at true scale.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Design-Time Guidelines&lt;/h2&gt;
&lt;p&gt;The structure and script address what happens. These habits reduce how many cases happen in the first place.&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Treat contracts like a public API.&lt;/strong&gt; Get their shape right before multiple types implement them. Every field you add later drags every implementer into the same push. Small, focused contracts are cheaper to change than broad ones.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Freeze keys once a type lands on any shared instance.&lt;/strong&gt; Key rename = delete + create. Change &lt;strong&gt;displayName&lt;/strong&gt; freely &amp;mdash; it&#39;s cosmetic. Treat &lt;strong&gt;key&lt;/strong&gt; as permanent from day one.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Prefer additive changes.&lt;/strong&gt; Adding a property is safe and localised. Deprecate and add rather than rename or delete.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Separate your content-model layer from your app code and templates.&lt;/strong&gt; The folder structure is what makes any detection approach tractable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Push in dependency order on fresh environments.&lt;/strong&gt; Property groups first, then contracts, then types that implement them.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Route true breaking changes through a full push.&lt;/strong&gt; Key renames, deletions, dropping a contract &amp;mdash; these are coordination events. Merge, let CI/CD do the authoritative full push, plan content migration separately.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; border-width: 1px; border-style: solid; width: 100%;&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top; background-color: rgb(206, 212, 217);&quot;&gt;
&lt;p&gt;&lt;strong&gt;What changed&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top; background-color: rgb(206, 212, 217);&quot;&gt;
&lt;p&gt;&lt;strong&gt;Dependency created&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top; background-color: rgb(206, 212, 217);&quot;&gt;
&lt;p&gt;&lt;strong&gt;Direction to explore&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;Field added to a &lt;strong&gt;contract&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;All implementing types (transitive)&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;Scan by contract folder + name &amp;mdash; reliability varies&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Property group&lt;/strong&gt; key changed&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;All types using that group key&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;Diff config + scan, or manual review at PR time&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Component&lt;/strong&gt; schema changed&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;All types embedding it as a typed property&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;Scan by component folder + name&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;Type added to &lt;strong&gt;allowedTypes&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;None &amp;mdash; stays in your diff&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;No extra action needed&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;Referenced type key &lt;strong&gt;renamed or deleted&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;All types naming it anywhere&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;Git-blob compare to detect + coordinated full push&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; border-width: 1px; border-style: solid; width: 100%;&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 47.6px;&quot;&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top; height: 47.6px; background-color: rgb(206, 212, 217);&quot;&gt;
&lt;p&gt;&lt;strong&gt;Environment&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top; height: 47.6px; background-color: rgb(206, 212, 217);&quot;&gt;
&lt;p&gt;&lt;strong&gt;Templates&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top; height: 47.6px; background-color: rgb(206, 212, 217);&quot;&gt;
&lt;p&gt;&lt;strong&gt;Content types + contracts&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top; height: 47.6px; background-color: rgb(206, 212, 217);&quot;&gt;
&lt;p&gt;&lt;strong&gt;Key renames&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 47.6px;&quot;&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top; height: 47.6px;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Developer instances&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top; height: 47.6px;&quot;&gt;
&lt;p&gt;Full push of template config&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top; height: 47.6px;&quot;&gt;
&lt;p&gt;Propose-review-push (adapted to your project)&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top; height: 47.6px;&quot;&gt;
&lt;p&gt;Never selective&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 47.6px;&quot;&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top; height: 47.6px;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Shared QA / Staging&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top; height: 47.6px;&quot;&gt;
&lt;p&gt;Full push on merge&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top; height: 47.6px;&quot;&gt;
&lt;p&gt;Full push on merge&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top; height: 47.6px;&quot;&gt;
&lt;p&gt;Full push after team coordination&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 67.2px;&quot;&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top; height: 67.2px;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Production&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top; height: 67.2px;&quot;&gt;
&lt;p&gt;CI/CD on release&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top; height: 67.2px;&quot;&gt;
&lt;p&gt;CI/CD on release&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top; height: 67.2px;&quot;&gt;
&lt;p&gt;Coordinated release + content migration&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;The Point&lt;/h2&gt;
&lt;p&gt;Part 1 gave you selective push &amp;mdash; it works, and it&#39;s still the right approach for the vast majority of day-to-day content type changes.&lt;/p&gt;
&lt;p&gt;This part maps the cases where it gets more complex: when a single-file change reaches further than the diff suggests. The five cases are real &amp;mdash; the CMS validates them this way and you will hit them. The folder structure and propose-review-push workflow are directions we think are worth exploring, not a proven system.&lt;/p&gt;
&lt;p&gt;We&#39;re publishing this partly because the answer isn&#39;t fully settled. If you&#39;ve solved any of these more cleanly &amp;mdash; or found that some of this doesn&#39;t hold up in practice &amp;mdash; that&#39;s exactly the kind of feedback this series is for.&lt;/p&gt;
&lt;p&gt;Know the cases. Structure the repo. Let the workflow propose, and you review.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;Have you hit any of these dependency cases on a project? Particularly curious whether the folder-structure approach holds up in practice, and whether anyone has a cleaner answer for the key-rename detection. Comments open below.&lt;/em&gt;&lt;/p&gt;</id><updated>2026-07-25T12:23:05.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Parallel Development in Optimizely CMS SaaS: A Smarter Way to Register Components</title><link href="https://world.optimizely.com/blogs/vipin-banka--learnings--insights/dates/2026/7/parallel-development-in-optimizely-cms-saas-a-smarter-way-to-register-components/" /><id>&lt;p&gt;&lt;strong&gt;&#128204; A note before you read:&lt;/strong&gt; The approach described in this article is not a replacement for Optimizely&amp;rsquo;s recommended out-of-the-box component registration pattern &amp;mdash; which is clean, explicit, and exactly right for most projects. This is written for teams of &lt;strong&gt;4 or more developers&lt;/strong&gt; working on &lt;strong&gt;parallel feature branches&lt;/strong&gt; who are finding that a shared registry file is becoming a source of repeated, low-value merge conflicts. If that&amp;rsquo;s not your situation yet, file this away for when it is.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;When Optimizely introduced code-first content modeling for CMS SaaS, they solved one of the biggest headaches in modern CMS development. Being able to define your content types in standard &lt;strong&gt;.tsx&lt;/strong&gt; files and push them via the CLI is a massive developer experience win.&lt;/p&gt;
&lt;p&gt;But modeling is only half the equation. On the frontend, you must map those content types to actual React components so the SDK can resolve and render them.&lt;/p&gt;
&lt;p&gt;The standard approach uses a central registration file &amp;mdash; typically in your Next.js layout or a dedicated registry file &amp;mdash; where you manually import every single component and content type, registering them with &lt;strong&gt;initContentTypeRegistry&lt;/strong&gt; and &lt;strong&gt;initReactComponentRegistry&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;// The classic static registration (e.g., src/components/registry.ts)
import { initContentTypeRegistry } from &#39;@optimizely/cms-sdk&#39;;
import { initReactComponentRegistry } from &#39;@optimizely/cms-sdk/react/server&#39;;

import HeroBanner, { HeroBannerContentType } from &#39;./HeroBanner&#39;;
import ArticlePage, { ArticlePageContentType } from &#39;./ArticlePage&#39;;
import GridContainer, { GridContainerContentType } from &#39;./GridContainer&#39;;

export function registerComponents() {
  initContentTypeRegistry([
    HeroBannerContentType,
    ArticlePageContentType,
    GridContainerContentType,
  ]);

  initReactComponentRegistry({
    resolver: {
      HeroBanner,
      ArticlePage,
      GridContainer,
    },
  });
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This explicit registration is an excellent default design. It&#39;s clean, type-safe, and self-documenting.&lt;/p&gt;
&lt;p&gt;But as your engineering team grows from one developer to five, ten, or twenty working in parallel, this central registry file starts to feel a bit crowded.&lt;/p&gt;
&lt;p&gt;Let&#39;s talk about the friction this creates &amp;mdash; and how a small shift in thinking can clean up your git history forever.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The Tragedy of the Common Registry File&lt;/h2&gt;
&lt;p&gt;Imagine a typical day on an enterprise development team.&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Developer A&lt;/strong&gt; is on a feature branch building a new &lt;strong&gt;PromoCard&lt;/strong&gt; component.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Developer B&lt;/strong&gt; is on a separate branch creating an &lt;strong&gt;AuthorBio&lt;/strong&gt; content type.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Developer C&lt;/strong&gt; is refactoring the &lt;strong&gt;NavigationMenu&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each developer creates their standalone component file. But to make it work in their respective sandbox environments, they &lt;em&gt;all&lt;/em&gt; have to touch the exact same central file &amp;mdash; &lt;strong&gt;src/components/registry.ts&lt;/strong&gt; &amp;mdash; to import and register their work.&lt;/p&gt;
&lt;p&gt;Everything works beautifully in isolation. The local tests pass, and the PRs are opened.&lt;/p&gt;
&lt;p&gt;But when the time comes to merge these branches back into &lt;strong&gt;main&lt;/strong&gt;, Git grinds to a halt. There&#39;s a merge conflict in &lt;strong&gt;registry.ts&lt;/strong&gt;. Because everyone modified the exact same import list on the exact same lines, the automated merge fails.&lt;/p&gt;
&lt;p&gt;Dealing with merge conflicts on actual code logic is a normal part of engineering. Dealing with merge conflicts on a &lt;em&gt;list of import statements&lt;/em&gt; is just tax. It slows down your CI/CD pipelines, frustrates developers, and adds zero business value.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The Mind Shift: Static Imports vs. Runtime Discovery&lt;/h2&gt;
&lt;p&gt;The default behavior of manually importing components in a static file is excellent for small projects or stable schemas. It provides absolute certainty about what is being loaded &amp;mdash; Optimizely&#39;s design here is intentional and solid.&lt;/p&gt;
&lt;p&gt;But as you scale parallel development, the question changes: &lt;em&gt;&quot;Do we really need to declare what files exist when our file system already knows?&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This is the mental model shift: &lt;strong&gt;move from manual, static registration to automated, runtime discovery.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If a developer places a valid component &amp;mdash; with its matching &lt;strong&gt;contentType&lt;/strong&gt; definition &amp;mdash; inside the &lt;strong&gt;src/components&lt;/strong&gt; folder, the project should automatically detect and register it. Your folder structure is the source of truth; your registry file should simply reflect that truth dynamically at build time.&lt;/p&gt;
&lt;p&gt;By generating this registry file automatically, we preserve all the power, safety, and type-safety of Optimizely&#39;s SDK while eliminating manual list maintenance &amp;mdash; and the inevitable merge conflicts that come with it &amp;mdash; entirely.&lt;/p&gt;
&lt;p&gt;The question for your team to consider: is the overhead of manually maintaining the registry file appropriate for the size and pace of your team? For a solo developer or a two-person squad, the static approach is perfect. For five or more developers running parallel feature tracks, automation becomes the right call.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Implementing Registry Generation&lt;/h2&gt;
&lt;p&gt;Instead of writing complex dynamic imports that can sometimes complicate React server-side rendering or bundle splitting, the most robust approach is a lightweight pre-build &lt;strong&gt;codegen script&lt;/strong&gt; that automatically compiles your &lt;strong&gt;registry.ts&lt;/strong&gt; file right before your app starts or builds.&lt;/p&gt;
&lt;p&gt;This is a well-established industry pattern that works perfectly with standard Next.js and React environments.&lt;/p&gt;
&lt;h3&gt;1. The Registry Generator Script&lt;/h3&gt;
&lt;p&gt;Create a native Node.js script in your project root: &lt;strong&gt;generate-registry.mjs&lt;/strong&gt;. It recursively scans &lt;strong&gt;src/components&lt;/strong&gt;, finds every component file, and writes a fully-resolved &lt;strong&gt;registry.ts&lt;/strong&gt;.&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;// generate-registry.mjs
import fs from &#39;fs&#39;;
import path from &#39;path&#39;;

const COMPONENTS_DIR = &#39;./src/components&#39;;
const OUTPUT_FILE = &#39;./src/components/registry.ts&#39;;

function getComponentFiles(dir, filesList = []) {
  if (!fs.existsSync(dir)) return filesList;

  const items = fs.readdirSync(dir);

  for (const item of items) {
    const fullPath = path.join(dir, item);
    const stat = fs.statSync(fullPath);

    if (stat.isDirectory()) {
      getComponentFiles(fullPath, filesList);
    } else if ((item.endsWith(&#39;.tsx&#39;) || item.endsWith(&#39;.ts&#39;)) &amp;amp;&amp;amp; !item.includes(&#39;registry&#39;)) {
      // Expects each file to export a default component + a named ContentType
      // e.g. export const HeroBannerContentType = contentType(...)
      filesList.push(fullPath);
    }
  }
  return filesList;
}

try {
  console.log(&#39;[Optimizely Registry] Scanning components...&#39;);
  const files = getComponentFiles(COMPONENTS_DIR);

  const imports = [];
  const contentTypes = [];
  const resolvers = [];

  files.forEach((file) =&amp;gt; {
    const relativePath = &#39;./&#39; + path.relative(path.dirname(OUTPUT_FILE), file)
      .replace(/\\/g, &#39;/&#39;)
      .replace(/\.tsx?$/, &#39;&#39;);

    const componentName = path.basename(file, path.extname(file));
    const contentTypeConst = `${componentName}ContentType`;

    imports.push(`import ${componentName}, { ${contentTypeConst} } from &#39;${relativePath}&#39;;`);
    contentTypes.push(`  ${contentTypeConst}`);
    resolvers.push(`    ${componentName}`);
  });

  const output = `// ==========================================================================
// AUTO-GENERATED &amp;mdash; do not edit manually. Changes will be overwritten.
// Run: node generate-registry.mjs  |  Source: generate-registry.mjs
// ==========================================================================

import { initContentTypeRegistry } from &#39;@optimizely/cms-sdk&#39;;
import { initReactComponentRegistry } from &#39;@optimizely/cms-sdk/react/server&#39;;

${imports.join(&#39;\n&#39;)}

export function registerComponents() {
  initContentTypeRegistry([
${contentTypes.join(&#39;,\n&#39;)},
  ]);

  initReactComponentRegistry({
    resolver: {
${resolvers.join(&#39;,\n&#39;)},
    },
  });
}
`;

  fs.writeFileSync(OUTPUT_FILE, output, &#39;utf8&#39;);
  console.log(`[Optimizely Registry] Done &amp;mdash; ${files.length} component(s) registered.`);

} catch (err) {
  console.error(&#39;[Optimizely Registry] Generation failed:&#39;, err.message);
  process.exit(1);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2. Wire It Into Your Lifecycle&lt;/h3&gt;
&lt;p&gt;Hook the script into your &lt;strong&gt;package.json&lt;/strong&gt; using npm&#39;s built-in &lt;strong&gt;pre&lt;/strong&gt; hooks. It runs automatically before both &lt;strong&gt;dev&lt;/strong&gt; and &lt;strong&gt;build&lt;/strong&gt; &amp;mdash; no manual step required.&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&quot;scripts&quot;: {
  &quot;predev&quot;: &quot;node generate-registry.mjs&quot;,
  &quot;prebuild&quot;: &quot;node generate-registry.mjs&quot;,
  &quot;dev&quot;: &quot;next dev&quot;,
  &quot;build&quot;: &quot;next build&quot;,
  &quot;start&quot;: &quot;next start&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then add the generated file to &lt;strong&gt;.gitignore&lt;/strong&gt;, since it is a build artifact:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;# .gitignore
src/components/registry.ts&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From this point on, no developer ever touches &lt;strong&gt;registry.ts&lt;/strong&gt; again. It is generated fresh on every &lt;strong&gt;npm run dev&lt;/strong&gt; and every CI/CD build run.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Why This Works on Local and CI/CD Alike&lt;/h2&gt;
&lt;p&gt;Because the generator runs as part of the standard dev/build lifecycle, it scales flawlessly across all stages without any developer intervention.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; border-width: 1px; border-style: solid;&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Scenario&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Static Registry&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Generated Registry&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Local Sandbox&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;Developers must manually add imports every time they create a component.&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;Creating a &lt;strong&gt;.tsx&lt;/strong&gt; file and running &lt;strong&gt;npm run dev&lt;/strong&gt; registers it automatically.&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Feature Branch&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;Every branch modifies the shared &lt;strong&gt;registry.ts&lt;/strong&gt;, creating Git conflicts on merge.&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;Branches only contain the component file itself. &lt;strong&gt;registry.ts&lt;/strong&gt; is Git-ignored. Merges are 100% clean.&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;&lt;strong&gt;CI/CD Pipeline&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;Static imports are read as-is. A missed import causes a silent runtime failure.&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 10px; border-style: solid; text-align: left; vertical-align: top;&quot;&gt;
&lt;p&gt;The generator scans the actual workspace. If the file exists in the repo, it gets registered.&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;Is This the Right Move for Your Team?&lt;/h2&gt;
&lt;p&gt;Runtime registry generation isn&#39;t a universal answer &amp;mdash; it&#39;s a tool for the right context. Here&#39;s how to think about it:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Stick with the static registry if:&lt;/strong&gt;&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;Your team is small (one to three developers)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Your schema is stable and rarely changes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You value absolute explicitness and prefer to control every import&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Consider dynamic generation if:&lt;/strong&gt;&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;You have four or more developers working on parallel feature branches&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You&#39;re frequently hitting merge conflicts on a single registration file&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You want your CI/CD pipeline to be self-healing rather than dependent on human memory&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Neither approach is wrong. The static model is exactly what Optimizely recommends out of the box &amp;mdash; and for good reason. The generated model is a team-scale evolution of that same idea, where automation takes on the maintenance burden.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;Optimizely&#39;s SDK gives you a clean, type-safe, and explicit way to map content types to React components. That design is intentional and it&#39;s solid.&lt;/p&gt;
&lt;p&gt;But as teams grow, automation should replace manual list-keeping. By shifting your mental model toward dynamic registry generation, you get the best of both worlds: the reliability and structure of Optimizely&#39;s component resolution, paired with a conflict-free, scalable parallel development workflow.&lt;/p&gt;
&lt;p&gt;It&#39;s a simple script with a meaningful impact on your daily developer experience. Give it a try on your next feature sprint and let the filesystem do the organizing.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;Are you running into registry conflicts on your Optimizely CMS SaaS projects? Have you found other ways to automate the component wiring? Share what&#39;s working for your team in the comments &amp;mdash; we&#39;d love to hear it.&lt;/em&gt;&lt;/p&gt;</id><updated>2026-07-21T05:42:00.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Parallel Development in Optimizely CMS SaaS: A Smarter Way to Push Content Models</title><link href="https://world.optimizely.com/blogs/vipin-banka--learnings--insights/dates/2026/7/parallel-development-in-optimizely-cms-saas-a-smarter-way-to-push-content-models/" /><id>&lt;p&gt;When Optimizely shipped the JavaScript SDK and CLI for CMS SaaS, they gave developers something pretty cool &amp;mdash; a code-first workflow for content modeling. Define your content types in &lt;strong&gt;.tsx&lt;/strong&gt; files, run &lt;strong&gt;npx @optimizely/cms-cli@latest push&lt;/strong&gt;, and your content models land in the CMS instance via the REST API.&lt;/p&gt;
&lt;p&gt;Clean. Fast. Developer-friendly.&lt;/p&gt;
&lt;p&gt;But here&#39;s the thing about developer tools &amp;mdash; the moment you hand them to a &lt;em&gt;team&lt;/em&gt;, the dynamics shift. And that&#39;s not a flaw in the tooling. It&#39;s just what happens when multiple people start pushing content models to the same environment at the same time.&lt;/p&gt;
&lt;p&gt;Let&#39;s talk about that.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The Reality of Parallel Development&lt;/h2&gt;
&lt;p&gt;Picture this: you&#39;re on a feature branch, building out a &lt;strong&gt;HeroBanner&lt;/strong&gt; component. Your teammate is on a separate branch, refining the &lt;strong&gt;ArticlePage&lt;/strong&gt; schema. Both of you are pointing your CLI at the same non-production CMS instance.&lt;/p&gt;
&lt;p&gt;You run &lt;strong&gt;push&lt;/strong&gt;. The CLI reads your &lt;strong&gt;optimizely.config.mjs&lt;/strong&gt;, scans the entire &lt;strong&gt;src/components/&lt;/strong&gt; directory, and attempts to sync &lt;em&gt;everything&lt;/em&gt; it finds &amp;mdash; your changes, your teammate&#39;s changes, and every other content type definition sitting in the codebase.&lt;/p&gt;
&lt;p&gt;Now, if your local branch doesn&#39;t have your teammate&#39;s latest work (because they haven&#39;t merged yet), the CLI sees a mismatch. It flags a conflict. You get an error.&lt;/p&gt;
&lt;p&gt;This is actually &lt;em&gt;good behavior&lt;/em&gt;. The CLI is protecting you from accidentally overwriting someone else&#39;s work. Optimizely built in a &lt;strong&gt;--force&lt;/strong&gt; flag for exactly this reason &amp;mdash; when you know what you&#39;re doing and want to push regardless, you can.&lt;/p&gt;
&lt;p&gt;But here&#39;s where the mental model needs to shift.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The Force Flag Solves the Wrong Problem (For This Scenario)&lt;/h2&gt;
&lt;p&gt;Let&#39;s be clear &amp;mdash; the &lt;strong&gt;--force&lt;/strong&gt; option is a legitimate and useful tool. It exists because there are real scenarios where you need to override what&#39;s on the remote instance. Schema migrations, breaking changes, cleanup operations &amp;mdash; force is the right call.&lt;/p&gt;
&lt;p&gt;But in a parallel development workflow, the question isn&#39;t &lt;em&gt;&quot;how do I push harder?&quot;&lt;/em&gt; &amp;mdash; it&#39;s &lt;em&gt;&quot;why am I pushing things I didn&#39;t change?&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Think about it. If you only modified &lt;strong&gt;HeroBanner.tsx&lt;/strong&gt;, why should the CLI even &lt;em&gt;look&lt;/em&gt; at &lt;strong&gt;ArticlePage.tsx&lt;/strong&gt;? Why should it care about 47 other component files that you never touched?&lt;/p&gt;
&lt;p&gt;The problem isn&#39;t conflict resolution. The problem is &lt;strong&gt;scope&lt;/strong&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;A Different Way to Think About It&lt;/h2&gt;
&lt;p&gt;Here&#39;s the mental shift: instead of treating the CLI push as a &lt;em&gt;full sync&lt;/em&gt; operation, treat it as a &lt;em&gt;deployment of your changes&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;This is how we already think about code deployments. Your CI/CD pipeline doesn&#39;t redeploy every microservice when you change one. Your database migration tool doesn&#39;t re-run every migration from the beginning. You deploy &lt;em&gt;what changed&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Content model pushes should work the same way.&lt;/p&gt;
&lt;p&gt;The good news? The CLI already supports this. The &lt;strong&gt;optimizely.config.mjs&lt;/strong&gt; file controls &lt;em&gt;which components the CLI processes&lt;/em&gt;. If you narrow the &lt;strong&gt;components&lt;/strong&gt; array to only the files you&#39;ve changed, the CLI will only push those content types. No conflicts with your teammate&#39;s work. No force flags needed. No wasted API calls.&lt;/p&gt;
&lt;p&gt;The trick is doing this &lt;em&gt;dynamically&lt;/em&gt; &amp;mdash; based on your actual Git branch changes.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Git as Your Scope Engine&lt;/h2&gt;
&lt;p&gt;Your version control system already knows exactly what you changed. A simple &lt;strong&gt;git diff&lt;/strong&gt; against your base branch gives you the precise list of modified &lt;strong&gt;.tsx&lt;/strong&gt; files. From there, it&#39;s straightforward to generate a temporary config file, push only those components, and clean up.&lt;/p&gt;
&lt;p&gt;Here&#39;s a Node.js script that handles the entire flow:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;// push-branch-changes.mjs
import { execSync } from &#39;child_process&#39;;
import fs from &#39;fs&#39;;
import path from &#39;path&#39;;

const baseBranch = process.argv[2] || &#39;origin/main&#39;;
const tempConfig = &#39;optimizely.config.temp.mjs&#39;;

try {
  console.log(`Checking changes against ${baseBranch}...`);

  const gitOutput = execSync(
    `git diff ${baseBranch} --name-only -- &quot;src/components/**/*.tsx&quot;`,
    { encoding: &#39;utf8&#39; }
  );

  const changedFiles = gitOutput
    .split(&#39;\n&#39;)
    .map(f =&amp;gt; f.trim())
    .filter(f =&amp;gt; f.length &amp;gt; 0 &amp;amp;&amp;amp; fs.existsSync(f));

  if (changedFiles.length === 0) {
    console.log(&#39;No component changes on this branch. Nothing to push.&#39;);
    process.exit(0);
  }

  console.log(`\nPushing ${changedFiles.length} changed component(s):`);
  changedFiles.forEach(f =&amp;gt; console.log(`  - ${f}`));

  // Generate a temporary config scoped to changed files only
  const paths = changedFiles.map(f =&amp;gt; `&#39;./${f}&#39;`).join(&#39;,\n    &#39;);
  const config = `import { buildConfig } from &#39;@optimizely/cms-sdk&#39;;

export default buildConfig({
  components: [
    ${paths}
  ],
});
`;

  fs.writeFileSync(path.resolve(tempConfig), config, &#39;utf8&#39;);

  // Push using the scoped config
  execSync(`npx @optimizely/cms-cli@latest push --config ${tempConfig}`, {
    stdio: &#39;inherit&#39;,
  });

  // Cleanup
  fs.unlinkSync(tempConfig);
  console.log(&#39;Done. Only your branch changes were pushed.&#39;);

} catch (err) {
  console.error(&#39;Push failed:&#39;, err.message);
  if (fs.existsSync(tempConfig)) fs.unlinkSync(tempConfig);
  process.exit(1);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Wire it up in your &lt;strong&gt;package.json&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&quot;scripts&quot;: {
  &quot;cms:push-branch&quot;: &quot;node push-branch-changes.mjs&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And run it:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;# Compare against origin/main (default)
npm run cms:push-branch

# Or compare against a different base
npm run cms:push-branch origin/develop&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&#39;s it. The script diffs your branch, generates a scoped config, pushes only your changes, and cleans up after itself.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;What This Actually Gets You&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Speed.&lt;/strong&gt; Instead of the CLI parsing and validating every content type in your project, it processes only the handful you touched. On larger codebases with dozens of content types, the difference is noticeable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Safety.&lt;/strong&gt; You literally cannot overwrite a teammate&#39;s content type because the CLI never sees it. There&#39;s no conflict to resolve, no force flag to debate, no Slack message asking &lt;em&gt;&quot;did someone just push over my changes?&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Focus.&lt;/strong&gt; Your push operation maps 1:1 to your pull request. What you changed in code is what gets pushed to the CMS. Nothing more, nothing less. That&#39;s easier to review, easier to debug, and easier to roll back.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Where This Fits in a Bigger Picture&lt;/h2&gt;
&lt;p&gt;This approach pairs well with a structured environment strategy. Consider this setup:&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; border-width: 1px; border-style: solid;&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border-width: 1px; border-style: solid;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Environment&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border-width: 1px; border-style: solid;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Who Pushes&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border-width: 1px; border-style: solid;&quot;&gt;
&lt;p&gt;&lt;strong&gt;How&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-width: 1px; border-style: solid;&quot;&gt;
&lt;p&gt;Developer instances&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border-width: 1px; border-style: solid;&quot;&gt;
&lt;p&gt;Individual devs&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border-width: 1px; border-style: solid;&quot;&gt;
&lt;p&gt;Branch-scoped push (this script)&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-width: 1px; border-style: solid;&quot;&gt;
&lt;p&gt;Shared QA/Staging&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border-width: 1px; border-style: solid;&quot;&gt;
&lt;p&gt;CI/CD pipeline&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border-width: 1px; border-style: solid;&quot;&gt;
&lt;p&gt;Full push on merge to main&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-width: 1px; border-style: solid;&quot;&gt;
&lt;p&gt;Production&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border-width: 1px; border-style: solid;&quot;&gt;
&lt;p&gt;CI/CD pipeline&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border-width: 1px; border-style: solid;&quot;&gt;
&lt;p&gt;Full push on release&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Developers get fast, isolated feedback loops on their own instances. The shared environments only receive content models that have been reviewed and merged. Production stays locked down behind your release process.&lt;/p&gt;
&lt;p&gt;The Optimizely CLI&#39;s full push behavior is &lt;em&gt;exactly right&lt;/em&gt; for CI/CD &amp;mdash; you want a complete sync when deploying from a merged, canonical branch. The branch-scoped approach fills the gap for the development phase, where speed and isolation matter more than completeness.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;The Optimizely CMS SaaS CLI is a solid tool. The force option exists for a reason and handles real conflicts well. But parallel development introduces a different kind of challenge &amp;mdash; not &lt;em&gt;&quot;how do I resolve conflicts&quot;&lt;/em&gt; but &lt;em&gt;&quot;how do I avoid creating them in the first place?&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;By scoping your pushes to only the content types you&#39;ve actually changed, you sidestep the problem entirely. Your Git branch becomes the source of truth for what gets pushed, and everyone on the team stays out of each other&#39;s way.&lt;/p&gt;
&lt;p&gt;It&#39;s a small script with a big impact on your daily workflow. Give it a try on your next feature branch.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;Have you tried a similar approach or found other ways to streamline parallel development with Optimizely CMS SaaS? We&#39;d love to hear what&#39;s working for your team.&lt;/em&gt;&lt;/p&gt;</id><updated>2026-07-20T09:04:26.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>The Silent Success: When Your Optimizely SaaS CMS Config Push Succeeds with &quot;0&quot; Changes</title><link href="https://world.optimizely.com/blogs/vipin-banka--learnings--insights/dates/2026/7/the-silent-success-why-your-optimizely-saas-cms-config-pushes-succeed-while-doing-absolutely-nothing/" /><id>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Picture this frustratingly common scenario in headless, code-first development with Optimizely SaaS CMS:&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ve defined a brilliant new element, block, or page type in your React codebase. You trigger your local push command. The console works its magic, spinning up green indicators and a clean finish:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code&gt;&#128228; Pushing content types...
✓ Configuration file uploaded
✅ Push succeeded.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But when you open the Optimizely CMS UI, navigate to &lt;strong&gt;Settings &amp;gt; Content Types&lt;/strong&gt;, and look for your new type... there is absolutely, crushing nothingness.&lt;/p&gt;
&lt;p&gt;You immediately jump into emergency triage mode. You double-check your &lt;strong&gt;.env&lt;/strong&gt; keys. You check permissions. Everything is correct. So why did the CLI say &quot;Success&quot; while doing absolutely nothing?&lt;/p&gt;
&lt;p&gt;This is the &lt;strong&gt;CLI Silent Empty Push&lt;/strong&gt; &amp;mdash; and to debug it, you have to work through a specific hierarchy of checks. Let&#39;s trace the journey from the most obvious suspects down to the real, silent culprit.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The &quot;Everything Looks Correct&quot; Checklist&lt;/h2&gt;
&lt;p&gt;Before tear-downs and code reviews, developers usually start with the obvious infrastructure questions. Here is the checklist of things you likely already verified:&lt;/p&gt;
&lt;h3&gt;&#128269; Check 1: Client ID &amp;amp; Client Secret&lt;/h3&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The Question:&lt;/strong&gt; Are your credentials expired or invalid?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;How to verify:&lt;/strong&gt; Run &lt;strong&gt;npx @optimizely/cms-cli@latest login&lt;/strong&gt;. If the connection is broken, the CLI will throw an authentication error immediately. If it says &quot;Authentication complete,&quot; your credentials are functional.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&#128269; Check 2: API Permissions &amp;amp; Impersonation Scopes&lt;/h3&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The Question:&lt;/strong&gt; Does your API Key have the rights to actually write schemas?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;How to verify:&lt;/strong&gt; Check &lt;strong&gt;Settings &amp;gt; API Keys&lt;/strong&gt; in the CMS UI. Ensure the key is active and possesses write scopes. If your key lacks permissions, a push would trigger a stark &lt;strong&gt;403 Forbidden&lt;/strong&gt; or &lt;strong&gt;401 Unauthorized&lt;/strong&gt; error in your terminal.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&#128269; Check 3: The Target Environment URL&lt;/h3&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The Question:&lt;/strong&gt; Are you accidentally pushing to the wrong sandbox or staging instance?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;How to verify:&lt;/strong&gt; Inspect your &lt;strong&gt;OPTIMIZELY_CMS_API_URL&lt;/strong&gt; environment variable. If you pushed to a different instance, the schemas would exist, just on the wrong server.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;If all of those are green, why is the CMS still empty?&lt;/h3&gt;
&lt;p&gt;If credentials, permissions, and host URLs are completely correct, and the terminal prints a successful &lt;strong&gt;200 OK&lt;/strong&gt;, you have officially bypassed standard configuration errors.&lt;/p&gt;
&lt;p&gt;The issue isn&#39;t security or network-related. It is a silent, 12-character path resolution discrepancy.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;What the Official Documentation Says&lt;/h2&gt;
&lt;p&gt;The official Optimizely &lt;a href=&quot;https://docs.developers.optimizely.com/content-management-system/v1.0.0-CMS-SaaS/docs/configure-javascript-sdk&quot;&gt;&lt;em&gt;Configure JavaScript SDK&lt;/em&gt;&lt;/a&gt; guide is explicit about where the configuration file should live. Step 4 reads:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;*&quot;Create an &lt;strong&gt;optimizely.config.mjs&lt;/strong&gt; file in the &lt;strong&gt;root of your project&lt;/strong&gt;.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The official project structure diagram reinforces this:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code&gt;.
├── src/
│   └── components/
├── .env
├── optimizely.config.mjs   &amp;larr; root level, explicitly
└── ...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The documentation does not cover what happens when you move this file into a subdirectory &amp;mdash; &lt;strong&gt;because it assumes you won&#39;t.&lt;/strong&gt; Developers following natural project organisation instincts &amp;mdash; a clean root, a monorepo structure, a dedicated &lt;strong&gt;config/&lt;/strong&gt; folder for multiple environment files &amp;mdash; deviate from this guidance without realising there is a hidden consequence.&lt;/p&gt;
&lt;p&gt;The CLI works perfectly when the file is at the root. The moment it moves into a subdirectory, globs silently resolve against the wrong base path, and the push uploads nothing.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The Real Culprit: The Glob Resolution Trap&lt;/h2&gt;
&lt;p&gt;In clean, modern JavaScript monorepos or well-structured project hierarchies, developers rarely leave configuration files floating in the root directory. To keep things tidy, we often organize configurations into subdirectories like &lt;strong&gt;.optimizely/&lt;/strong&gt; or &lt;strong&gt;config/&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;For instance, you might move your CMS configuration to:&lt;/p&gt;
&lt;pre&gt;&lt;strong&gt;config/optimizely.config.mjs&lt;/strong&gt;&lt;/pre&gt;
&lt;p&gt;And inside that nested configuration file, you write your relative component search paths:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { buildConfig } from &#39;@optimizely/cms-sdk&#39;;

export default buildConfig({
  components: [&#39;./src/content-types/**/*.ts&#39;],
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is where the silent trap springs. Here is the golden rule of the Optimizely CMS CLI:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;The CLI resolves glob patterns inside a configuration file relative to the location of the configuration file itself, not relative to the root directory where you ran the CLI command.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Because the configuration file lives inside the &lt;strong&gt;config/&lt;/strong&gt; subdirectory, the CLI reads &lt;strong&gt;./src/content-types/...&lt;/strong&gt; as relative to &lt;strong&gt;config/&lt;/strong&gt;. It starts searching for your schemas here:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;config/src/content-types/**/*.ts&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;That subdirectory path does not exist. The CLI scans the folder, matches &lt;strong&gt;0 files&lt;/strong&gt;, and finds exactly &lt;strong&gt;0 schemas&lt;/strong&gt; to process.&lt;/p&gt;
&lt;h3&gt;Why did the CLI report success?&lt;/h3&gt;
&lt;p&gt;The Optimizely CLI is highly permissive. If a glob pattern matches zero files, it does not throw an error or fail the build. It assumes you just have a clean configuration slate, packs up an empty schema payload, and uploads it.&lt;/p&gt;
&lt;p&gt;The CMS API receives a valid, perfectly formed (but empty) configuration payload, updates nothing, and returns an &lt;strong&gt;HTTP 200 OK&lt;/strong&gt;. The CLI reports &lt;strong&gt;Configuration file uploaded&lt;/strong&gt; because the upload itself &lt;em&gt;did&lt;/em&gt; succeed.&lt;/p&gt;
&lt;p&gt;The result: a completely silent, completely successful push of nothing.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The Fix&lt;/h2&gt;
&lt;p&gt;The simplest fix is to follow the official documentation &amp;mdash; keep your configuration file at the &lt;strong&gt;project root&lt;/strong&gt; where the CLI expects it:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;# ❌ Nested (globs resolve relative to config/ &amp;mdash; finds nothing)
npx @optimizely/cms-cli config push config/optimizely.config.mjs

# ✅ Root level (globs resolve relative to root &amp;mdash; finds your files)
npx @optimizely/cms-cli config push optimizely.config.mjs&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you have a strong reason to keep the config file inside a subdirectory &amp;mdash; such as a monorepo setup or managing multiple environment configurations &amp;mdash; you can adjust the glob paths inside the config to step up one level:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { buildConfig } from &#39;@optimizely/cms-sdk&#39;;

export default buildConfig({
  // ../  steps up from config/ back to the project root
  components: [&#39;../src/content-types/**/*.ts&#39;],
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both approaches work. The root-level approach aligns with official documentation and is the least error-prone for single-app projects. The &lt;strong&gt;../&lt;/strong&gt; prefix approach is useful for monorepos or multi-environment setups where nesting is intentional.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Reusable Debugging Checklist for Teams&lt;/h2&gt;
&lt;p&gt;Bookmark this order-of-operations checklist for the next time a developer on your team runs into missing content types after a successful-looking push:&lt;/p&gt;
&lt;ol class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;CLI Authentication Test&lt;/strong&gt; &amp;mdash; Run &lt;strong&gt;npx @optimizely/cms-cli@latest login&lt;/strong&gt; to rule out expired credentials.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;API Key Scope Verification&lt;/strong&gt; &amp;mdash; Confirm your key has write access under &lt;strong&gt;Settings &amp;gt; API Keys&lt;/strong&gt; in the CMS.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Environment URL Check&lt;/strong&gt; &amp;mdash; Verify &lt;strong&gt;OPTIMIZELY_CMS_API_URL&lt;/strong&gt; points to the instance you are currently viewing.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Config File Location Audit&lt;/strong&gt; &amp;mdash; Is your &lt;strong&gt;optimizely.config.mjs&lt;/strong&gt; (or any dynamically generated config) in the project root? If it is nested in a subdirectory, check whether the glob paths inside use &lt;strong&gt;../&lt;/strong&gt; to compensate.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Glob Match Validation&lt;/strong&gt; &amp;mdash; Test whether your glob patterns actually match any files before pushing:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;import { globSync } from &#39;fast-glob&#39;;
const matched = globSync(&#39;./src/content-types/**/*.ts&#39;);
if (matched.length === 0) {
  console.error(&#39;❌ Zero files matched. Config push will be empty.&#39;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Visual Builder Composition&lt;/strong&gt; &amp;mdash; If your type appears in &lt;strong&gt;Settings &amp;gt; Content Types&lt;/strong&gt; but not in the Visual Builder editor, go to that type&#39;s &lt;strong&gt;Settings&lt;/strong&gt; tab and enable &lt;strong&gt;&quot;Available for composition in Visual Builder&quot;&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;Key Takeaway&lt;/h2&gt;
&lt;p&gt;The Optimizely developer documentation is clear: &lt;strong&gt;optimizely.config.mjs&lt;/strong&gt; belongs at the root of your project. When it lives there, everything works exactly as expected. When it moves &amp;mdash; even for completely legitimate reasons &amp;mdash; the CLI&#39;s glob resolution silently breaks, the push uploads an empty payload, and the CMS returns a perfectly polite &lt;strong&gt;200 OK&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The fix is simple. The behaviour, however, is not documented &amp;mdash; which is precisely what makes it one of the most confusing silent failures in a SaaS CMS headless setup.&lt;/p&gt;
&lt;p&gt;One line change. Types appear instantly.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;Have you run into other silent failure modes when working with Optimizely SaaS CMS content type workflows? Share your experience in the comments &amp;mdash; this kind of institutional knowledge keeps teams shipping faster.&lt;/em&gt;&lt;/p&gt;</id><updated>2026-07-13T07:32:17.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Architecting an Enterprise-Grade Development Pipeline in Optimizely SaaS CMS</title><link href="https://world.optimizely.com/blogs/vipin-banka--learnings--insights/dates/2026/7/architecting-an-enterprise-grade-development-pipeline-in-optimizely-saas-cms/" /><id>&lt;p&gt;Most enterprise teams show up to Optimizely SaaS CMS with a clear roadmap for their release pipeline: DEV &amp;rarr; QA &amp;rarr; Stage &amp;rarr; Prod. Four logical environments to manage active coding, quality assurance, business sign-off, and live operations.&lt;/p&gt;
&lt;p&gt;When setting up your initial cloud-native topology, the question immediately arises: how do we align this 4-stage pipeline with our SaaS CMS instances in a way that is automated, secure, and team-safe?&lt;/p&gt;
&lt;p&gt;This is a practical guide for every dev lead, architect, and project manager looking to design a high-velocity, team-safe development workflow. We&#39;ve mapped out the key architectural strategies, branching patterns, and deployment automation tactics to make your Optimizely SaaS CMS pipeline production-ready.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;First, Let&#39;s Talk About What &quot;Separate Applications&quot; Can and Can&#39;t Do&lt;/h2&gt;
&lt;p&gt;Before we dive into environment mapping, it&#39;s critical to understand how Optimizely SaaS CMS handles resource isolation: &lt;strong&gt;applications and environments are not the same thing.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Within a single CMS SaaS instance, you can create multiple &lt;strong&gt;Applications&lt;/strong&gt; &amp;mdash; each with its own:&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;Content tree and start page hierarchy&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Preview URLs and hostname configuration&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Routing and application root settings&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is incredibly powerful for multi-site and multi-brand strategies. But here is the architectural boundary: &lt;strong&gt;content types are scoped to the instance, not the application.&lt;/strong&gt; When a developer pushes a new content type or modifies an existing schema, that change is immediately live across every application on that instance. There is no branching, draft mode, or per-application isolation for schemas.&lt;/p&gt;
&lt;p&gt;Because schemas are instance-wide, a shared development or testing instance requires a deliberate promotion strategy to ensure that active development doesn&#39;t disrupt running QA or staging cycles.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Analyzing the Topologies: Pros and Cons of Environment Sharing&lt;/h2&gt;
&lt;p&gt;If your initial setup is configured with three physical SaaS instances, achieving a 4-stage pipeline requires pairing two of your logical phases onto a single instance. There are three primary ways to design this topology, each with distinct architectural consequences.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;Topology 1: Shared DEV &amp;amp; QA (The Schema Bottleneck Pattern)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Overall Assessment: Not recommended for parallel development teams&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In this pattern, the first instance is shared by developers and QA testers, while Staging and Production remain fully isolated.&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt; Developers iterate constantly. Every new feature requires adding content types, modifying properties, or updating validation rules. In a shared setup, these schema changes land on the instance immediately.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The Impact on QA:&lt;/strong&gt; If a developer pushes an unverified or breaking schema change mid-sprint, it can instantly destabilize the testing environment, blocking QA automation runs and invalidating manual test cycles.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; While workable for small, highly coordinated teams (1-2 developers), this pattern quickly becomes a bottleneck for larger teams working on parallel streams.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;Topology 2: DEV | Shared QA &amp;amp; Stage (The Recommended Pattern)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Overall Assessment: The gold standard for velocity and stability&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This pattern keeps your DEV environment fully isolated, while QA testing and User Acceptance Testing (UAT/Staging) share the middle instance.&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt; Developers have complete freedom to experiment, iterate, and break things on the DEV instance. Schemas are promoted to the QA+Stage instance only through a controlled, automated deployment gate.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Managing the Share:&lt;/strong&gt; Because QA and UAT/Staging are typically &lt;strong&gt;sequential, not simultaneous&lt;/strong&gt;, they rarely require different schema versions at the same time. You complete functional QA, sign off on the release branch, and then hand over to business stakeholders for UAT on the same schema.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;How Separate Applications Help:&lt;/strong&gt; By creating two separate &lt;strong&gt;Applications&lt;/strong&gt; within this single instance, you cleanly isolate your content and preview layers:&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;QA Application:&lt;/strong&gt; Points to your QA frontend deployment, using QA-specific content and automated test trees.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;UAT Application:&lt;/strong&gt; Points to your Staging/UAT frontend deployment, showcasing pristine demo content for business stakeholders.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; This is the most robust topology for a 3-instance setup. It completely protects developer velocity and ensures production-level safety.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;Topology 3: DEV | QA | Shared Stage &amp;amp; Production (The High-Risk Pattern)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Overall Assessment: Avoid entirely&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In this pattern, DEV and QA are fully isolated, but business sign-off (Staging/UAT) happens on the production instance.&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The Danger:&lt;/strong&gt; Even with separate CMS applications isolating your live content tree from your draft UAT tree, &lt;strong&gt;schemas are still instance-wide&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The Impact:&lt;/strong&gt; Any content type modification or configuration change made during UAT is immediately live on production. A single configuration oversight during a stakeholder review session can trigger an immediate production incident.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; Production stability is non-negotiable. This topology should never be used.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;The Pipeline Comparison Matrix&lt;/h2&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px;&quot;&gt;
&lt;p&gt;Pipeline Strategy&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px;&quot;&gt;
&lt;p&gt;DEV+QA Shared / Stage / Prod&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px;&quot;&gt;
&lt;p&gt;DEV / QA+Stage Shared / Prod (Recommended)&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px;&quot;&gt;
&lt;p&gt;DEV / QA / Stage+Prod Shared&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px;&quot;&gt;
&lt;p&gt;Developer Freedom&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px;&quot;&gt;
&lt;p&gt;❌ Constrained&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px;&quot;&gt;
&lt;p&gt;✅ Full&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px;&quot;&gt;
&lt;p&gt;✅ Full&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px;&quot;&gt;
&lt;p&gt;QA Schema Stability&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px;&quot;&gt;
&lt;p&gt;❌ Unstable&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px;&quot;&gt;
&lt;p&gt;✅ Stable&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px;&quot;&gt;
&lt;p&gt;✅ Stable&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px;&quot;&gt;
&lt;p&gt;UAT/Stakeholder Experience&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px;&quot;&gt;
&lt;p&gt;✅ Clean&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px;&quot;&gt;
&lt;p&gt;⚠️ Shares with QA&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px;&quot;&gt;
&lt;p&gt;❌ Shares with Prod&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px;&quot;&gt;
&lt;p&gt;Production Safety&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px;&quot;&gt;
&lt;p&gt;✅ Safe&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px;&quot;&gt;
&lt;p&gt;✅ Safe&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px;&quot;&gt;
&lt;p&gt;❌ Dangerous&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px;&quot;&gt;
&lt;p&gt;Application Isolation Helps?&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px;&quot;&gt;
&lt;p&gt;⚠️ Minimally&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px;&quot;&gt;
&lt;p&gt;✅ Meaningfully&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px;&quot;&gt;
&lt;p&gt;⚠️ Not enough&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px;&quot;&gt;
&lt;p&gt;Large Team Suitability&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px;&quot;&gt;
&lt;p&gt;&#128308; Poor&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px;&quot;&gt;
&lt;p&gt;&#128994; Excellent&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px;&quot;&gt;
&lt;p&gt;&#128308; Unacceptable&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;Implementing Automated Schema Promotion&lt;/h2&gt;
&lt;p&gt;To make this pipeline robust, you should eliminate manual schema management. The recommended approach is to define all content types in code &amp;mdash; inside your frontend components (e.g., .tsx files) &amp;mdash; and let the &lt;strong&gt;Optimizely CMS CLI&lt;/strong&gt; handle promotion programmatically.&lt;/p&gt;
&lt;h3&gt;1. Integrate CLI Sync into Your CI/CD Build&lt;/h3&gt;
&lt;p&gt;Your Git branch is the source of truth, and your CI/CD pipeline is the gate. Configure separate API credentials for each CMS instance in your pipeline&#39;s environment variables:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;# In your CI/CD pipeline (e.g., GitHub Actions, Azure DevOps)
# On deploy to the staging environment:
OPTIMIZELY_CMS_CLIENT_ID=${{ secrets.QA_CMS_CLIENT_ID }}
OPTIMIZELY_CMS_CLIENT_SECRET=${{ secrets.QA_CMS_CLIENT_SECRET }}

npx @optimizely/cms-cli@latest sync&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This ensures that schema changes only land on your QA+Stage instance when code is merged and deployed to your release branch &amp;mdash; completely insulating testers from mid-sprint developer changes.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Technical Note:&lt;/strong&gt; The CLI operates as a one-way push, reading content definitions from code and synchronizing them to the target CMS instance. This reinforces the best practice that schemas should always be version-controlled in Git.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;2. Map Git Branches directly to CMS Instances&lt;/h3&gt;
&lt;p&gt;Establishing a clear one-to-one relationship between your repository branches and your CMS targets ensures a seamless release flow:&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; border-width: 1px; border-style: solid;&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;border-width: 1px; text-align: left; vertical-align: top; border-style: solid;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Git Branch&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; text-align: left; vertical-align: top; border-style: solid;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Target Frontend&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; text-align: left; vertical-align: top; border-style: solid;&quot;&gt;
&lt;p&gt;&lt;strong&gt;CLI target (CMS Instance)&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-width: 1px; text-align: left; vertical-align: top; border-style: solid;&quot;&gt;
&lt;p&gt;feature/*&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; text-align: left; vertical-align: top; border-style: solid;&quot;&gt;
&lt;p&gt;Local / DEV Deploy&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; text-align: left; vertical-align: top; border-style: solid;&quot;&gt;
&lt;p&gt;DEV Instance&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-width: 1px; text-align: left; vertical-align: top; border-style: solid;&quot;&gt;
&lt;p&gt;release/* or main&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; text-align: left; vertical-align: top; border-style: solid;&quot;&gt;
&lt;p&gt;QA &amp;amp; Stage Deploys&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; text-align: left; vertical-align: top; border-style: solid;&quot;&gt;
&lt;p&gt;QA+Stage Shared Instance&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-width: 1px; text-align: left; vertical-align: top; border-style: solid;&quot;&gt;
&lt;p&gt;production&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; text-align: left; vertical-align: top; border-style: solid;&quot;&gt;
&lt;p&gt;Live Production Deploy&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; text-align: left; vertical-align: top; border-style: solid;&quot;&gt;
&lt;p&gt;Prod Instance&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;Aligning SaaS Architecture with Team Requirements&lt;/h2&gt;
&lt;p&gt;This approach reflects the cloud-native, headless architecture of modern SaaS platforms: the CMS backend is tightly integrated with global content APIs, real-time preview engines, and the Optimizely Graph layer.&lt;/p&gt;
&lt;p&gt;By utilizing branch-gated CI/CD pipelines and native CMS Applications, teams can deliver enterprise-grade release management that feels fast, automated, and safe.&lt;/p&gt;
&lt;p&gt;For large global organizations where a strictly isolated 4-environment physical topology is required by compliance or parallel testing tracks, we recommend discussing your roadmap early with your Optimizely account team. Additional non-production instances can easily be provisioned, and framing the discussion around team velocity and schema-gated testing ensures a highly collaborative path forward.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Summary of Best Practices&lt;/h2&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Differentiate applications from environments:&lt;/strong&gt; Use separate Applications to isolate content trees, but remember schemas are instance-wide.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Isolate development first:&lt;/strong&gt; Keep your DEV instance fully separated so developers have complete freedom to innovate and experiment.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Automate schema delivery:&lt;/strong&gt; Define content types in code and use the &lt;strong&gt;Optimizely CMS CLI&lt;/strong&gt; to synchronize schemas at build time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Gate with Git:&lt;/strong&gt; Align your branching strategy directly with your CMS targets to turn your pipeline into a hands-free, automated release gate.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;How has your team structured its headless deployment pipeline? Share your architecture tips and lessons learned in the comments below!&lt;/em&gt;&lt;/p&gt;</id><updated>2026-07-12T02:38:20.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Bynder DAM Connector for Optimizely SaaS CMS: Improved Metadata Property Synchronization</title><link href="https://world.optimizely.com/blogs/vipin-banka--learnings--insights/dates/2026/7/bynder-dam-connector-for-optimizely-saas-cms-improved-metadata-property-synchronization/" /><id>&lt;p&gt;While working with the &lt;strong&gt;Bynder DAM Connector for Optimizely SaaS CMS&lt;/strong&gt;, one of the key areas I explored was how Bynder asset metadata is synchronized into &lt;strong&gt;Optimizely Graph&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The connector already makes it possible to synchronize Bynder assets into Optimizely and expose them through Graph. However, during implementation validation, we came across an important limitation around metadata property synchronization.&lt;/p&gt;
&lt;h2&gt;The Previous Limitation&lt;/h2&gt;
&lt;p&gt;Earlier, when configuring the synchronization between Bynder and Optimizely, only &lt;strong&gt;Bynder date-type metadata properties&lt;/strong&gt; were available for syncing into the Optimizely Graph schema.&lt;/p&gt;
&lt;p&gt;This created a challenge because real-world DAM implementations rarely depend only on date fields.&lt;/p&gt;
&lt;p&gt;Most enterprise DAM metadata models use a combination of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Text fields&lt;/li&gt;
&lt;li&gt;Date fields&lt;/li&gt;
&lt;li&gt;Single-select fields&lt;/li&gt;
&lt;li&gt;Multi-select fields&lt;/li&gt;
&lt;li&gt;Taxonomy-like option fields&lt;/li&gt;
&lt;li&gt;Classification fields&lt;/li&gt;
&lt;li&gt;Asset usage fields&lt;/li&gt;
&lt;li&gt;Product/category metadata&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a website implementation, this metadata is often required by the frontend application for rendering, filtering, searching, or making delivery decisions.&lt;/p&gt;
&lt;p&gt;For example, teams may need metadata such as:&lt;/p&gt;
&lt;div class=&quot;___77lcry0 f10pi13n&quot;&gt;
&lt;div class=&quot;___1hewfwb f10pi13n f7oukh6 f5p0z4x&quot;&gt;
&lt;div&gt;
&lt;div class=&quot;scriptor-component-code-block _7CA39p32MJD4WzQj430bEw== scriptor-codeblock-virtualized&quot;&gt;
&lt;div class=&quot;_8cbhMyAb-O4gAE0YhMz5Fw== qMbZ7GayOiqoFAta+ROcfQ== FXk3ZkPfVa3dK4NENK0BuA== RV6aOXRSOKYambFOGypRUQ==&quot;&gt;
&lt;div class=&quot;PR-eQAguLtV9jbLyecurDA== IWL7hnrJGkNIEXimV4iK3g== wyvwRXSwbmbyvZ+nt0gDjg==&quot;&gt;
&lt;div class=&quot;i6fZNmFRGK4PfVuJHFCJ7A==&quot;&gt;
&lt;div class=&quot;JnZEfplZ8eOYvZUyrK1wMQ== j0h+RXQ+O2kKSwB+JVlQsg== J61nEcw0wYMHfeNRQL3FdA==&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Asset type&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Product family&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Language&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Region&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Usage rights&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Document category&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Video category&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Public/private status&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Website readiness&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;If only date fields are available during synchronization, then the DAM metadata model cannot be fully represented in Optimizely Graph.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;What Has Improved&lt;/h2&gt;
&lt;p&gt;The Optimizely team has now improved the Bynder connector metadata synchronization behavior.&lt;/p&gt;
&lt;p&gt;Metadata properties from Bynder can now be selected across multiple field types, including:&lt;/p&gt;
&lt;div class=&quot;___77lcry0 f10pi13n&quot;&gt;
&lt;div class=&quot;___1hewfwb f10pi13n f7oukh6 f5p0z4x&quot;&gt;
&lt;div&gt;
&lt;div class=&quot;scriptor-component-code-block _7CA39p32MJD4WzQj430bEw== scriptor-codeblock-virtualized&quot;&gt;
&lt;div class=&quot;_8cbhMyAb-O4gAE0YhMz5Fw== qMbZ7GayOiqoFAta+ROcfQ== FXk3ZkPfVa3dK4NENK0BuA== RV6aOXRSOKYambFOGypRUQ==&quot;&gt;
&lt;div class=&quot;PR-eQAguLtV9jbLyecurDA== IWL7hnrJGkNIEXimV4iK3g== wyvwRXSwbmbyvZ+nt0gDjg==&quot;&gt;
&lt;div class=&quot;i6fZNmFRGK4PfVuJHFCJ7A==&quot;&gt;
&lt;div class=&quot;JnZEfplZ8eOYvZUyrK1wMQ== j0h+RXQ+O2kKSwB+JVlQsg== J61nEcw0wYMHfeNRQL3FdA==&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Date&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Text&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Single select&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Multiple select&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;h2&gt;How Different Property Types Behave&lt;/h2&gt;
&lt;h4&gt;1) Date Properties&lt;/h4&gt;
&lt;p&gt;Date properties synchronize the date value from Bynder. In Optimizely content type the type will become a string type.&lt;/p&gt;
&lt;h4&gt;2) Text Properties&lt;/h4&gt;
&lt;p&gt;Text properties synchronize the value entered by the author or DAM user in Bynder.&lt;/p&gt;
&lt;h4&gt;3) Single Select and Multiple Select Properties&lt;/h4&gt;
&lt;p&gt;Single-select and multiple-select properties behave differently.&lt;/p&gt;
&lt;p&gt;For these fields, the synchronized values represent the &lt;strong&gt;database names&lt;/strong&gt;&amp;nbsp;for the selected options, not the display labels shown to users in the Bynder interface.&lt;/p&gt;
&lt;p&gt;For example,&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Bynder option label: Case Study&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Synchronized value:&amp;nbsp;Case_Study&lt;/p&gt;
&lt;p&gt;This can be useful for backend logic, integrations, filtering, or stable system-level matching.&lt;/p&gt;
&lt;p&gt;However, these values may not always be ideal for direct frontend rendering, especially if the frontend needs human-readable labels or multilingual labels&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;h2&gt;Important Note on Labels&lt;/h2&gt;
&lt;p&gt;For &lt;strong&gt;single-select&lt;/strong&gt; and &lt;strong&gt;multiple-select&lt;/strong&gt; metadata properties, if the frontend application needs the actual option labels, those labels should be retrieved separately using the &lt;strong&gt;Bynder API&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This is especially important when labels are maintained in multiple languages.&lt;/p&gt;
&lt;p&gt;This label-resolution topic deserves its own separate discussion because it involves option IDs, database names, display labels, multilingual labels, and frontend rendering decisions.&lt;/p&gt;
&lt;hr /&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;What About Option IDs?&lt;/h2&gt;
&lt;p&gt;The connector also provides a standard property called:&lt;/p&gt;
&lt;div class=&quot;___77lcry0 f10pi13n&quot;&gt;
&lt;div class=&quot;___1hewfwb f10pi13n f7oukh6 f5p0z4x&quot;&gt;
&lt;div&gt;
&lt;div class=&quot;scriptor-component-code-block _7CA39p32MJD4WzQj430bEw== scriptor-codeblock-virtualized&quot;&gt;
&lt;div class=&quot;_8cbhMyAb-O4gAE0YhMz5Fw== qMbZ7GayOiqoFAta+ROcfQ== FXk3ZkPfVa3dK4NENK0BuA== RV6aOXRSOKYambFOGypRUQ==&quot;&gt;
&lt;div class=&quot;PR-eQAguLtV9jbLyecurDA== IWL7hnrJGkNIEXimV4iK3g== wyvwRXSwbmbyvZ+nt0gDjg==&quot;&gt;
&lt;div class=&quot;i6fZNmFRGK4PfVuJHFCJ7A==&quot;&gt;
&lt;div class=&quot;JnZEfplZ8eOYvZUyrK1wMQ== j0h+RXQ+O2kKSwB+JVlQsg== J61nEcw0wYMHfeNRQL3FdA==&quot;&gt;
&lt;div class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;
&lt;ul&gt;
&lt;li&gt;PropertyOptions&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This property returns a consolidated list of option IDs selected across available single-select and multi-select metadata fields.&lt;/p&gt;
&lt;p&gt;Following screenshot shows how Grpah stores the data in propertyOptions field.&lt;/p&gt;
&lt;figure class=&quot;image align-left&quot;&gt;&lt;img style=&quot;border-width: 1px; border-style: solid;&quot; src=&quot;/link/71858b04167a4faa915999c7188bb96b.aspx?1783753202657&quot; alt=&quot;Graph stores all selected options ids inside &amp;quot;propertyOptions&amp;quot;&quot; width=&quot;711&quot; height=&quot;156&quot; /&gt;
&lt;figcaption&gt;Graph stroes all seleted option ids inside &quot;propertyOptions&quot; property&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;div&gt;
&lt;h2&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2&gt;&amp;nbsp;&lt;/h2&gt;
&lt;div&gt;
&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;Why This Fix Matters&lt;/h2&gt;
&lt;p&gt;This improvement makes the Bynder DAM Connector more practical for real-world Optimizely SaaS CMS implementations.&lt;/p&gt;
&lt;p&gt;It allows teams to bring more useful metadata into Optimizely Graph without being limited only to date fields.&lt;/p&gt;
&lt;p&gt;This helps with:&lt;/p&gt;
&lt;div class=&quot;___77lcry0 f10pi13n&quot;&gt;
&lt;div class=&quot;___1hewfwb f10pi13n f7oukh6 f5p0z4x&quot;&gt;
&lt;div class=&quot;scriptor-component-code-block _7CA39p32MJD4WzQj430bEw== scriptor-codeblock-virtualized&quot;&gt;
&lt;div class=&quot;_8cbhMyAb-O4gAE0YhMz5Fw== qMbZ7GayOiqoFAta+ROcfQ== FXk3ZkPfVa3dK4NENK0BuA== RV6aOXRSOKYambFOGypRUQ==&quot;&gt;
&lt;div class=&quot;PR-eQAguLtV9jbLyecurDA== IWL7hnrJGkNIEXimV4iK3g== wyvwRXSwbmbyvZ+nt0gDjg==&quot;&gt;
&lt;div class=&quot;i6fZNmFRGK4PfVuJHFCJ7A==&quot;&gt;
&lt;div class=&quot;JnZEfplZ8eOYvZUyrK1wMQ== j0h+RXQ+O2kKSwB+JVlQsg== J61nEcw0wYMHfeNRQL3FdA==&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Better asset classification&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Cleaner frontend logic&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Improved filtering&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Better integration support&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;More flexible content delivery&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Reduced custom workarounds&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;The key improvement is that implementation teams can now synchronize more metadata property types from Bynder into Optimizely Graph and decide which metadata is useful for CMS, frontend, or integration scenarios.&amp;nbsp;
&lt;div&gt;
&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;You Do Not Need to Sync Everything&lt;/h2&gt;
&lt;p&gt;Another important point is that metadata synchronization does not need to be all-or-nothing.&lt;/p&gt;
&lt;p&gt;Implementation teams can choose which Bynder metadata properties should be synchronized into Optimizely.&lt;/p&gt;
&lt;p&gt;This is important because not every DAM metadata field is useful for website rendering or content delivery.&lt;/p&gt;
&lt;p&gt;For example, a DAM may contain metadata for:&lt;/p&gt;
&lt;div class=&quot;___77lcry0 f10pi13n&quot;&gt;
&lt;div class=&quot;___1hewfwb f10pi13n f7oukh6 f5p0z4x&quot;&gt;
&lt;div&gt;
&lt;div class=&quot;scriptor-component-code-block _7CA39p32MJD4WzQj430bEw== scriptor-codeblock-virtualized&quot;&gt;
&lt;div class=&quot;_8cbhMyAb-O4gAE0YhMz5Fw== qMbZ7GayOiqoFAta+ROcfQ== FXk3ZkPfVa3dK4NENK0BuA== RV6aOXRSOKYambFOGypRUQ==&quot;&gt;
&lt;div class=&quot;PR-eQAguLtV9jbLyecurDA== IWL7hnrJGkNIEXimV4iK3g== wyvwRXSwbmbyvZ+nt0gDjg==&quot;&gt;
&lt;div class=&quot;i6fZNmFRGK4PfVuJHFCJ7A==&quot;&gt;
&lt;div class=&quot;JnZEfplZ8eOYvZUyrK1wMQ== j0h+RXQ+O2kKSwB+JVlQsg== J61nEcw0wYMHfeNRQL3FdA==&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Internal workflows&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Asset production&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Legal approval&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Agency ownership&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Archive management&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Regional campaigns&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Website delivery&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Only a subset of those fields may be needed in Optimizely Graph.&lt;/p&gt;
&lt;p&gt;The better approach is to intentionally select the metadata fields that are required for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Frontend rendering&lt;/li&gt;
&lt;li&gt;Content authoring&lt;/li&gt;
&lt;li&gt;Filtering&lt;/li&gt;
&lt;li&gt;Search&lt;/li&gt;
&lt;li&gt;Asset classification&lt;/li&gt;
&lt;li&gt;Delivery rules&lt;/li&gt;
&lt;li&gt;Public/restricted asset decisions&lt;/li&gt;
&lt;li&gt;Video or document handling&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This avoids unnecessary metadata noise and keeps the Graph schema cleaner.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;Application User Interface for field Mapping&lt;/h2&gt;
&lt;p&gt;Here is a screenshot form the application for field mapping step which shows the simple user intrface to select what metadata properties from Bynder needs to be synced in Optimizely Graph.&lt;/p&gt;
&lt;p&gt;In below screenshot:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;All custom metadata fields are indicated with an asterisk (*) &lt;em&gt;at the end of the metadata property name e.g. AltText*&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;You can choose which property you want to keep into your schema or not so that sync will only store the relevant data in Optimizely Graph.&lt;/li&gt;
&lt;li&gt;You can not enter the property names. Application will automatcially generate that for you based on your property name and prepending that with &quot;property_&quot;.&lt;/li&gt;
&lt;li&gt;As you can see in below screenshot the PropertyOptions is standard Bynder asset property, there is no asterisk (*) with it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img style=&quot;border-width: 1px; border-style: solid;&quot; src=&quot;/link/59c02bbfdeed45f5ba2431bb3bb5e394.aspx?1783749874850&quot; width=&quot;1103&quot; height=&quot;1033&quot; /&gt;&lt;/p&gt;
&lt;div&gt;
&lt;p&gt;&lt;strong&gt;Asset data synced in Grpah (including the standard and the custom properties)&amp;nbsp;&lt;br /&gt;&lt;/strong&gt;*This screenshot is added just for illustration purpose only&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img style=&quot;border-width: 1px; border-style: solid;&quot; src=&quot;/link/460ca686863f41b087c124c09a031596.aspx&quot; alt=&quot;&quot; width=&quot;719&quot; height=&quot;1384&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;h2&gt;Recommended Implementation Approach&lt;/h2&gt;
&lt;p&gt;Before enabling broad metadata synchronization, teams should define a simple metadata sync strategy.&lt;/p&gt;
&lt;p&gt;Recommended steps:&lt;/p&gt;
&lt;div class=&quot;___77lcry0 f10pi13n&quot;&gt;
&lt;div class=&quot;___1hewfwb f10pi13n f7oukh6 f5p0z4x&quot;&gt;
&lt;div&gt;
&lt;div class=&quot;scriptor-component-code-block _7CA39p32MJD4WzQj430bEw== scriptor-codeblock-virtualized&quot;&gt;
&lt;div class=&quot;_8cbhMyAb-O4gAE0YhMz5Fw== qMbZ7GayOiqoFAta+ROcfQ== FXk3ZkPfVa3dK4NENK0BuA== RV6aOXRSOKYambFOGypRUQ==&quot;&gt;
&lt;div class=&quot;PR-eQAguLtV9jbLyecurDA== IWL7hnrJGkNIEXimV4iK3g== wyvwRXSwbmbyvZ+nt0gDjg==&quot;&gt;
&lt;div class=&quot;i6fZNmFRGK4PfVuJHFCJ7A==&quot;&gt;
&lt;div class=&quot;JnZEfplZ8eOYvZUyrK1wMQ== j0h+RXQ+O2kKSwB+JVlQsg== J61nEcw0wYMHfeNRQL3FdA==&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Review available Bynder metadata properties.&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Identify which fields are required by Optimizely CMS, Optimizely Graph, or the frontend application.&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Decide which fields should be synchronized.&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Confirm how each field type appears in Optimizely Graph.&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;For single-select and multiple-select fields, decide whether database values are sufficient.&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;If frontend labels are required, plan label resolution using Bynder API.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;___77lcry0 f10pi13n&quot;&gt;
&lt;div class=&quot;___1hewfwb f10pi13n f7oukh6 f5p0z4x&quot;&gt;
&lt;div&gt;
&lt;div class=&quot;scriptor-component-code-block _7CA39p32MJD4WzQj430bEw== scriptor-codeblock-virtualized&quot;&gt;
&lt;div class=&quot;_8cbhMyAb-O4gAE0YhMz5Fw== qMbZ7GayOiqoFAta+ROcfQ== FXk3ZkPfVa3dK4NENK0BuA== RV6aOXRSOKYambFOGypRUQ==&quot;&gt;
&lt;div class=&quot;PR-eQAguLtV9jbLyecurDA== IWL7hnrJGkNIEXimV4iK3g== wyvwRXSwbmbyvZ+nt0gDjg==&quot;&gt;
&lt;div class=&quot;i6fZNmFRGK4PfVuJHFCJ7A==&quot;&gt;
&lt;div class=&quot;JnZEfplZ8eOYvZUyrK1wMQ== j0h+RXQ+O2kKSwB+JVlQsg== J61nEcw0wYMHfeNRQL3FdA==&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;Example Metadata Sync Strategy&lt;/h2&gt;
&lt;p&gt;For a website implementation, the team may decide to sync fields such as:&lt;/p&gt;
&lt;div class=&quot;___77lcry0 f10pi13n&quot;&gt;
&lt;div class=&quot;___1hewfwb f10pi13n f7oukh6 f5p0z4x&quot;&gt;
&lt;div&gt;
&lt;div class=&quot;scriptor-component-code-block _7CA39p32MJD4WzQj430bEw== scriptor-codeblock-virtualized&quot;&gt;
&lt;div class=&quot;_8cbhMyAb-O4gAE0YhMz5Fw== qMbZ7GayOiqoFAta+ROcfQ== FXk3ZkPfVa3dK4NENK0BuA== RV6aOXRSOKYambFOGypRUQ==&quot;&gt;
&lt;div class=&quot;PR-eQAguLtV9jbLyecurDA== IWL7hnrJGkNIEXimV4iK3g== wyvwRXSwbmbyvZ+nt0gDjg==&quot;&gt;
&lt;div class=&quot;i6fZNmFRGK4PfVuJHFCJ7A==&quot;&gt;
&lt;div class=&quot;JnZEfplZ8eOYvZUyrK1wMQ== j0h+RXQ+O2kKSwB+JVlQsg== J61nEcw0wYMHfeNRQL3FdA==&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Asset title&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Asset description&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Asset type&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Language&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Region&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Public/private classification&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Website-ready flag&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Video ID&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Document category&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Product association&lt;/li&gt;
&lt;li class=&quot;tY5faFHEOPAHuO+tqkg+jA== DhgBbNGs9GW303hvyw6gHA==&quot;&gt;Application association&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;But avoid syncing fields that are only relevant to internal DAM operations.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;Why This Is a Good Improvement&lt;/h2&gt;
&lt;p&gt;This connector update improves the practical usability of Bynder DAM metadata inside Optimizely SaaS CMS implementations.&lt;/p&gt;
&lt;p&gt;It helps teams:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use richer metadata in Optimizely Graph&lt;/li&gt;
&lt;li&gt;Avoid custom mapping workarounds&lt;/li&gt;
&lt;li&gt;Keep frontend logic cleaner&lt;/li&gt;
&lt;li&gt;Use business-readable labels where needed&lt;/li&gt;
&lt;li&gt;Still access option IDs through &lt;strong&gt;&quot;propertyOptions&quot;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Select only the metadata fields that matter&lt;/li&gt;
&lt;li&gt;Better support real enterprise DAM models&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;h2&gt;Final Thought&lt;/h2&gt;
&lt;p&gt;This connector improvement is a valuable step forward for teams integrating &lt;strong&gt;Bynder DAM with Optimizely SaaS CMS&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Being able to synchronize more metadata property types into Optimizely Graph gives implementation teams more flexibility and reduces the need for custom workarounds.&lt;/p&gt;
&lt;p&gt;The key takeaway is simple:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Bynder DAM Connector can now synchronize more metadata property types into Optimizely Graph, but teams should still be intentional about which metadata they sync and how those values will be used.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For single-select and multiple-select fields, the synchronized values are useful for system logic, but frontend-friendly labels may still require a separate lookup through the Bynder API.&lt;/strong&gt;&lt;/p&gt;
&lt;/div&gt;</id><updated>2026-07-11T07:31:39.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Optimizely Content JS SDK v2.1.0 — What&#39;s New and Why It Matters</title><link href="https://world.optimizely.com/blogs/vipin-banka--learnings--insights/dates/2026/7/optimizely-content-js-sdk-v2.1.0--what-actually-changed-and-why-it-matters" /><id>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;v2.1.0 of the Optimizely Content JS SDK and CLI landed on July 7, 2026. This is a substantial release bringing a wave of capabilities for developers building headless experiences on Optimizely CMS SaaS &amp;mdash; including content type contracts, programmatic application management, a new project scaffolding command, and OpenTelemetry observability.&lt;/p&gt;
&lt;p&gt;Alongside these major features, the release addresses critical stability fixes that resolve key edge cases in content fetching, previews, and routing. Below is a deep dive into what has changed, why these updates matter, and how they improve your development workflow.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Content Type Sprawl and the New Contract Enhancements&lt;/h2&gt;
&lt;p&gt;If you&#39;ve been building with the SDK for a while, you&#39;ve probably felt this. Your content model grows. You have a Blog content type, a News Article, a Press Release. They&#39;re different types, but they share a bunch of the same properties &amp;mdash; &lt;strong&gt;category&lt;/strong&gt;, &lt;strong&gt;tags&lt;/strong&gt;, &lt;strong&gt;author&lt;/strong&gt;, &lt;strong&gt;publishedDate&lt;/strong&gt;. So you define those properties three separate times across three separate &lt;strong&gt;contentType()&lt;/strong&gt; calls. Then a product manager asks you to add a region field to all of them, and you make the same change in three places and hope you didn&#39;t miss one.&lt;/p&gt;
&lt;p&gt;While you could previously declare shared interfaces, &lt;strong&gt;v2.1.0 introduces major enhancements to how the SDK handles content type contracts&lt;/strong&gt; at runtime to eliminate this duplicate work:&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Allowed and restricted types on contracts&lt;/strong&gt; &amp;mdash; specify exactly which content types can or must implement a contract&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Automatic GraphQL fragment generation&lt;/strong&gt; &amp;mdash; when you query a contracted type, the SDK auto-generates the correct inline fragments so all implementing types return their contracted fields without manual query writing&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Component fallback mechanism&lt;/strong&gt; &amp;mdash; if a content item implements a contract but you haven&#39;t registered a specific React component for that particular type, the SDK now falls back to a contract-level component rather than rendering nothing. Silent blank renders caused by missing component registrations were a real source of confusion &amp;mdash; this addresses that.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On the CLI side, the &lt;strong&gt;pull&lt;/strong&gt; command now generates and validates contract definitions as part of your code generation step. One sync, and your contracts are in code.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/episerver/content-js-sdk/pull/292&quot;&gt;&lt;em&gt;CMS-46677, PR #292&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Programmatic Application Management via CLI&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;The Context: &lt;/strong&gt;The SDK and CLI provide a highly automated workflow for managing content types in code, but CMS applications (the configuration mapping your frontend hostnames to CMS start pages) historically required manual configuration in the CMS admin UI.&lt;/p&gt;
&lt;p&gt;For teams running multiple environments (Development, Staging, Production) or spinning up ephemeral environments for feature branches, this manual step was a deployment bottleneck. If a team member created an environment, they had to manually configure the application in the UI or routing wouldn&#39;t resolve. It wasn&#39;t version-controlled, automated, or easily reproducible.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Solution: &lt;/strong&gt;v2.1.0 introduces full &lt;strong&gt;application type definitions and validation&lt;/strong&gt; to the SDK and exposes three new programmatic management commands in the CLI:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;cms-cli createApplication    # Create a new CMS application programmatically
cms-cli updateApplication    # Update an existing application&#39;s configuration
cms-cli checkApplications    # Validate registered applications against your local config&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em class=&quot;far4bl _667h42&quot;&gt;**Refer to the &lt;a class=&quot;_667h42 _1l4j4nad _1c9dtv3 _1by2b93 _1140xq1 _667h42&quot; href=&quot;https://docs.developers.optimizely.com/content-management-system/v1.0.0-CMS-SaaS/docs/configure-javascript-sdk&quot;&gt;official CLI documentation&lt;/a&gt;&amp;nbsp;for exact command syntax and options.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Your application configuration can now live in code with the rest of your schema. For teams running automated CI/CD pipelines, this closes a significant orchestration gap.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/episerver/content-js-sdk/pull/333&quot;&gt;&lt;em&gt;CMS-51342, PR #333&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;A Seamless Editing Experience with NextPreviewComponent&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;The Context:&lt;/strong&gt;In headless setups, keeping the live preview fast and interactive is critical for content editors. However, standard preview routing setups in Next.js App Router projects often triggered full page reloads upon saving content drafts in the CMS. Editors had to wait for the entire route to re-render and often lost their scroll position or current tab context.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Solution:&lt;/strong&gt;v2.1.0 ships the &lt;strong&gt;NextPreviewComponent&lt;/strong&gt;, a preview component specifically optimized for the Next.js App Router rendering lifecycle:&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Optimized save-event listener&lt;/strong&gt; &amp;mdash; hooks into the content-saved webhook efficiently to detect draft saves immediately without polling&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Soft refresh support&lt;/strong&gt; &amp;mdash; updates draft content on-screen in place. The editor sees their changes reflect instantly without a full browser reload or losing scroll position&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This significantly reduces the friction of the editorial feedback loop, making visual editing in CMS SaaS feel smooth and instant.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/episerver/content-js-sdk/pull/319&quot;&gt;&lt;em&gt;CMS-52407, PR #319&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Observability Built In: OpenTelemetry Support&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;The Context: &lt;/strong&gt;Performance tuning in decoupled architectures is notoriously difficult. If a page load feels laggy, it can be hard to identify whether the delay is happening during client-side hydration, SDK query construction, network latency to Optimizely Graph, or &lt;span class=&quot;far4bl _667h42&quot;&gt;the query processing time within Optimizely Graph&lt;/span&gt;. Without deep instrumentation, developers are left guessing or manually wrapping every&amp;nbsp;&lt;strong&gt;getContentByPath()&lt;/strong&gt; call in custom tracing blocks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Solution: &lt;/strong&gt;The SDK now features native &lt;strong&gt;OpenTelemetry (OTEL) tracing and metrics&lt;/strong&gt;. If your hosting environment or application infrastructure runs an OTEL-compatible collector (such as Datadog, New Relic, Honeycomb, Grafana, or Jaeger), the SDK&#39;s fetch layer will automatically emit trace spans.&lt;/p&gt;
&lt;p&gt;You can now natively profile the duration of your content queries, see exactly how long Optimizely Graph takes to respond, and spot any nested reference fetches that are impacting your latency budget.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/episerver/content-js-sdk/pull/309&quot;&gt;&lt;em&gt;CMS-50199, PR #309&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Getting Started Faster with create-opti-app&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;The Context: &lt;/strong&gt;Historically, starting a new project on CMS SaaS required cloning a complete demo repository, stripping out demo content and styling, configuring environment variables from scratch, and adjusting boilerplates. It lacked a lightweight, standardized onboarding command.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Solution: &lt;/strong&gt;The CLI now features a dedicated onboarding and scaffolding utility:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;npx @optimizely/cms-cli@latest create-opti-app&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This utility lets you quickly scaffold a clean, working project from three officially maintained starter templates:&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Stride&lt;/strong&gt; &amp;mdash; The modern recommended starter preconfigured for Visual Builder&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Alloy&lt;/strong&gt; &amp;mdash; The classic Optimizely demonstration site&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;TanStack&lt;/strong&gt; &amp;mdash; TanStack Router + React for projects opting out of Next.js&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These templates are maintained alongside the core SDK, ensuring they stay aligned with the latest framework best practices.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/episerver/content-js-sdk/pull/382&quot;&gt;&lt;em&gt;CMS-49618, PR #382&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Key Stability and Bug Fixes&lt;/h2&gt;
&lt;p&gt;Behind the major feature updates, v2.1.0 delivers key bug fixes addressing critical routing, caching, and serialization edge cases.&lt;/p&gt;
&lt;h3&gt;Correcting getContent() Versioning Behavior&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt; In certain configurations, the &lt;strong&gt;getContent()&lt;/strong&gt; method (which retrieves a content item by its programmatic ID) was returning the&amp;nbsp;&lt;strong&gt;draft version&lt;/strong&gt; of a page or block to anonymous public visitors rather than the active published version. If an editor had an in-progress draft saved in the CMS, those unapproved changes &lt;span class=&quot;far4bl _667h42&quot;&gt;could unintentionally appear on the live website&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt; The SDK has been corrected to ensure &lt;strong&gt;getContent()&lt;/strong&gt; strictly returns the published version of content for public delivery, safeguarding editorial workflows.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/episerver/content-js-sdk/pull/307&quot;&gt;&lt;em&gt;CMS-52026, PR #307&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;Resolving getContentByPath() 404s on Simple Addresses&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt; Optimizely CMS allows editors to assign a &quot;simple address&quot; to any page (e.g., a short URL alias like &lt;strong&gt;/about&lt;/strong&gt; instead of a long hierarchical path). When a page used a simple address, &lt;strong&gt;getContentByPath()&lt;/strong&gt; was throwing a 404 error, breaking routing for pages where custom slugs had been configured.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt; Path-resolution logic has been updated to correctly resolve pages using CMS simple addresses, ensuring routing works smoothly across all customized URLs.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/episerver/content-js-sdk/pull/312&quot;&gt;&lt;em&gt;CMS-52209, PR #312&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;Resolving Empty DAM Asset References in Compositions&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt; When assets from Digital Asset Management (DAM) systems were referenced inside Visual Builder compositions (experiences structured using rows, columns, and sections), the returned fields occasionally resolved as &lt;strong&gt;null&lt;/strong&gt;&amp;nbsp;or empty. The query was executing successfully, but the auto-generated GraphQL query was missing the necessary inline fragments to serialize the nested DAM properties.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt; The query generator has been updated to guarantee proper DAM asset fragment resolution inside experience compositions.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/episerver/content-js-sdk/pull/318&quot;&gt;&lt;em&gt;CMS-52404, PR #318&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;Circular Reference Support in Code Generation&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt; In complex content models, types frequently reference each other circularly (for example, a &lt;strong&gt;Category&lt;/strong&gt; type containing a &lt;strong&gt;parentCategory&lt;/strong&gt; property of type &lt;strong&gt;Category&lt;/strong&gt;, or a &lt;strong&gt;MenuItem&lt;/strong&gt; pointing to child &lt;strong&gt;MenuItems&lt;/strong&gt;). When the CLI encountered these models, the code generator would enter an infinite loop or fail to compile the output.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt; Both the SDK and CLI have been updated to support circular and self-referential content types by outputting string-based type references at the boundary of a detected cycle, allowing code generation to compile cleanly.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/episerver/content-js-sdk/pull/409&quot;&gt;SDK: CMS-53737, PR #409 &lt;/a&gt;&amp;middot; &lt;a href=&quot;https://github.com/episerver/content-js-sdk/pull/305&quot;&gt;CLI: CMS-44353, PR #305&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;Consistent RichText HTML Entity Decoding&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt; Certain encoded HTML entities (such as non-breaking spaces &lt;strong&gt;&amp;amp;nbsp;&lt;/strong&gt;, em dashes &lt;strong&gt;&amp;amp;ndash;&lt;/strong&gt;, or custom characters) inside &lt;strong&gt;RichText&lt;/strong&gt; properties were decoded inconsistently, sometimes rendering raw entity markup directly to the page.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt; The entity decoding logic inside the &lt;strong&gt;RichText&lt;/strong&gt; parser has been standardized to ensure consistent rendering across all HTML entities.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/episerver/content-js-sdk/pull/353&quot;&gt;&lt;em&gt;CMS-53226, PR #353&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;Preview Mode: Synchronized Element Focus and Attributes&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt; In preview mode, two visual editing issues impacted the editor experience:&lt;/p&gt;
&lt;ol class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;Selecting an element in the Visual Builder sidebar didn&#39;t reliably focus or highlight that corresponding element inside the live iframe.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Visual editing attributes (&lt;strong&gt;data-epi-*&lt;/strong&gt; metadata) were missing from custom &lt;strong&gt;OptimizelyComposition&lt;/strong&gt; and &lt;strong&gt;OptimizelyGridSection&lt;/strong&gt; wrappers, rendering some on-page edit boundaries inactive.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt; The preview attribute delivery and sidebar communication have been fully synchronized. On-page edit boundaries now map perfectly to the visual structure, and sidebar element selections correctly shift iframe focus.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/episerver/content-js-sdk/pull/346&quot;&gt;Focus: CMS-52749, PR #346 &lt;/a&gt;&amp;middot; &lt;a href=&quot;https://github.com/episerver/content-js-sdk/pull/393&quot;&gt;Attributes: CMS-53437, PR #393&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;Fallback Resolution for types ending in &quot;...Property&quot;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt; If a custom content type name ended with the suffix &lt;strong&gt;&quot;Property&quot;&lt;/strong&gt; (e.g., &lt;strong&gt;HeroImageProperty&lt;/strong&gt;, the component registry&#39;s fallback resolver incorrectly classified it as a system property type rather than a content element, failing to resolve a fallback component.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt; The matching algorithms have been refined to ensure custom content types ending in &quot;Property&quot; correctly evaluate and resolve fallback layouts.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/episerver/content-js-sdk/pull/304&quot;&gt;&lt;em&gt;CMS-51995, PR #304&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Additional Improvements&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; border-width: 1px; border-spacing: 5px; border-style: solid;&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Change&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Base type properties in queries&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/episerver/content-js-sdk/pull/392&quot;&gt;CMS-41847, PR #392&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;Some inherited base type fields were missing from auto-generated GraphQL queries, requiring manual query extensions&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;All base type properties are now automatically included&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Locale on ContentType&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/episerver/content-js-sdk/pull/400&quot;&gt;CMS-53704, PR #400&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;No way to specify locale at the type definition level for multilingual setups&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;&lt;strong&gt;ContentType&lt;/strong&gt; and &lt;strong&gt;RICHTEXT_PRESET&lt;/strong&gt; now accept an optional &lt;strong&gt;locale&lt;/strong&gt; parameter&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Section fragments in compositions&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/episerver/content-js-sdk/pull/388&quot;&gt;CMS-53436, PR #388&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;Sections inside Visual Builder experiences didn&#39;t generate proper GraphQL fragments&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;Section fragment handling added to &lt;strong&gt;OptimizelyComposition&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Display template for experiences&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/episerver/content-js-sdk/pull/335&quot;&gt;CMS-52750, PR #335&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;Top-level experience content didn&#39;t respect its assigned display template&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;Display template resolution now works for top-level experiences&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Type re-exports&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/episerver/content-js-sdk/pull/405&quot;&gt;CMS-53656, PR #405&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;Missing re-exports caused broken &lt;strong&gt;.d.ts&lt;/strong&gt; declaration files in consuming projects&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;Type re-exports added for proper declaration file generation&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Empty contracts config&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/episerver/content-js-sdk/pull/342&quot;&gt;CMS-52849, PR #342&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;Passing an empty contracts array in config threw an error&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;Empty contracts are now allowed without errors&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Self-signed certificates (CLI)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/episerver/content-js-sdk/pull/322&quot;&gt;CMS-52494, PR #322&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;CLI couldn&#39;t connect to CMS instances behind self-signed certs in dev/staging&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;Self-signed certificate support added&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;&lt;strong&gt;RichText editor settings (CLI)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/episerver/content-js-sdk/pull/348&quot;&gt;CMS-52547, PR #348&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;No way to configure editor settings for RichText properties via the CLI&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;Editor settings for RichText are now supported&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Duplicate applications (CLI)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/episerver/content-js-sdk/pull/403&quot;&gt;CMS-53768, PR #403&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;CLI threw errors when multiple applications shared entry points or had missing entry points&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;Duplicate handling and missing entry point filtering added&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Preview URL with inProcessWebsite (CLI)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/episerver/content-js-sdk/pull/351&quot;&gt;CMS-53120, PR #351&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;Preview URLs didn&#39;t resolve correctly when using &lt;strong&gt;inProcessWebsite&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border-style: solid; border-width: 1px; text-align: left; vertical-align: top; padding: 1px;&quot;&gt;
&lt;p&gt;URL resolution logic fixed&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;How to Upgrade&lt;/h2&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;# Upgrade the SDK
npm install @optimizely/cms-sdk@2.1.0

# Upgrade the CLI
npm install -D @optimizely/cms-cli@2.1.0&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are no breaking changes in this release. Your existing &lt;strong&gt;contentType()&lt;/strong&gt; schemas, registry configurations, and React component mappings continue to work without modification.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Full Changelog&lt;/h2&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/episerver/content-js-sdk/releases/tag/%40optimizely%2Fcms-sdk%402.1.0&quot;&gt;&lt;strong&gt;@optimizely/cms-sdk@2.1.0&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/episerver/content-js-sdk/releases/tag/%40optimizely%2Fcms-cli%402.1.0&quot;&gt;&lt;strong&gt;@optimizely/cms-cli@2.1.0&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em class=&quot;far4bl _667h42&quot;&gt;This post is a community-authored summary based on the&amp;nbsp;&lt;a class=&quot;_667h42 _1l4j4nad _1c9dtv3 _1by2b93 _1140xq1 _667h42&quot; href=&quot;https://github.com/episerver/content-js-sdk/releases/tag/%40optimizely%2Fcms-sdk%402.1.0&quot;&gt;official v2.1.0 release notes&lt;/a&gt;. For authoritative guidance, always refer to the official Optimizely documentation.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;</id><updated>2026-07-08T03:20:06.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Exploring Asset Lifecycle Management Approaches for Bynder and Optimizely SaaS CMS</title><link href="https://world.optimizely.com/blogs/vipin-banka--learnings--insights/dates/2026/7/exploring-asset-lifecycle-management-approaches-for-bynder-and-optimizely-saas-cms/" /><id>&lt;p&gt;&lt;span class=&quot;far4bl _667h42&quot;&gt;Note: This is Part 3 of our Bynder integration series. For setup and filtering prerequisites, see &lt;a class=&quot;_667h42 _1l4j4nac _1c9dtv3 _1by2b93 _1140xq1 _667h42&quot; href=&quot;/link/4f215cd334254dfcbc121d5f4e20edd6.aspx&quot;&gt;Part 1&lt;/a&gt;&amp;nbsp;and&amp;nbsp;&lt;a class=&quot;_667h42 _1l4j4nac _1c9dtv3 _1by2b93 _1140xq1 _667h42&quot; href=&quot;/link/30275ac47c4843148eeda8b7d6ed282a.aspx&quot;&gt;Part 2&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;In my previous article, I explored approaches for controlling asset ingestion from Bynder into Optimizely SaaS CMS through metadata-based filtering.&lt;/p&gt;
&lt;p&gt;Filtering assets before they enter the platform is an important first step, but in many enterprise DAM integrations, it only addresses part of the lifecycle.&lt;/p&gt;
&lt;p&gt;Once assets have been synchronized, a different set of challenges begins to emerge:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What happens when an asset&#39;s metadata changes after synchronization?&lt;/li&gt;
&lt;li&gt;How should previously synchronized assets be handled when they no longer meet business rules?&lt;/li&gt;
&lt;li&gt;How can organizations govern assets across Development, Staging, and Production environments?&lt;/li&gt;
&lt;li&gt;How can teams safely determine whether an asset is still being used before removing it?&lt;/li&gt;
&lt;li&gt;When does it make sense to extend an existing integration versus building a completely custom connector?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These questions gradually shift the conversation from asset synchronization to asset lifecycle management.&lt;/p&gt;
&lt;p&gt;This article explores some architectural considerations and potential approaches that may help address those challenges.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;&lt;strong&gt;The Asset Lifecycle Challenge&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Synchronization is rarely a one-time event.&amp;nbsp;Assets constantly evolve throughout their lifetime.&amp;nbsp;&lt;br /&gt;A typical lifecycle might look something like this:&lt;/p&gt;
&lt;div class=&quot;___77lcry0 f10pi13n&quot;&gt;
&lt;div class=&quot;___1hewfwb f10pi13n f7oukh6 f5p0z4x&quot;&gt;
&lt;div class=&quot;fui-FluentProvider codeBlock-7100fui-FluentProviderr6av ___1jk74h0 fzk5nv6 f6n0el9 f7x6x6c f1a9mlr7 flj889f f13g0yyb ffzn0d2&quot;&gt;
&lt;div class=&quot;scriptor-component-code-block NhtKn7PZLzk24rduw4FJVQ== scriptor-codeblock-virtualized&quot;&gt;
&lt;div class=&quot;UkrLoQNicnNa5hsNIz3QLw== SpjJXGAclwYtfY+SokpSTg== ZF-Q2MvOY8aWSpGWW7iKRQ== anUhzybDvd7+8CBdKK7ukg==&quot;&gt;
&lt;div class=&quot;AHKpBuU8t+rAbDNImLlOHQ== lHY5JJVFnU8S-jx-OGwJkg== IUtI8YkF5Dl2LTlcjMDTwg==&quot;&gt;
&lt;div class=&quot;___149cboo fssjx5q f11d9hfk fj2zk3l fmyxm6j f1ldmben f1rkeeuc&quot;&gt;
&lt;div class=&quot;___svqwvq0 fssjx5q f11d9hfk fj2zk3l f1ldmben fzdgvog f19k5jia fzv833z f1r8x5cv f1687457 fe71wc6 f1wco4xj f103jgab f12fk6ok f4j985t fv86erk f1b4dd42 f1qwtq3x f13gvrpj f132if0v f1vblqk2 fkxsc7s f18j3zqo f1386a7m f1w3nze9 f14pe09c f4skf1h f1lq0dgc f1jgwha3 fbf5vau fc8703c&quot;&gt;
&lt;div class=&quot;___ysqij10 fonrc96 f1d3un5n fdk4ulh f1cijgmw fzb3wgo fssjx5q f11d9hfk fee5icb fr1uqzy&quot;&gt;
&lt;div class=&quot;___sv9ola0 fj2zk3l f1f4vkoj scriptor-instance-681&quot;&gt;
&lt;div class=&quot;scriptor-canvas scriptor-styled-scrollbar focus-container&quot;&gt;
&lt;div class=&quot;scriptor-pageContainer&quot;&gt;
&lt;div class=&quot;scriptor-pageFrame scriptor-firstPage scriptor-webView scriptor-content-visibility-auto&quot;&gt;
&lt;div class=&quot;scriptor-code-editor scriptor-code-wrap-on scriptor-pageBody scriptor-webViewPage&quot;&gt;
&lt;div class=&quot;scriptor-paragraph&quot;&gt;&lt;strong&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Asset Created&amp;nbsp; &amp;gt;&amp;nbsp; &lt;/span&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Asset Synchronized&amp;nbsp; &amp;gt;&amp;nbsp; &lt;/span&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Asset Referenced by Content&amp;nbsp; &amp;gt;&amp;nbsp; Asset &lt;/span&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Metadata Updated&amp;nbsp; &amp;gt;&amp;nbsp; &lt;/span&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Visibility Rules Changed&amp;nbsp; &amp;gt;&amp;nbsp; &lt;/span&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Environment Assignment Changed&amp;nbsp; &amp;gt;&amp;nbsp; &lt;/span&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Asset No Longer Valid&lt;/span&gt;&lt;/strong&gt;&lt;/div&gt;
&lt;div class=&quot;scriptor-paragraph&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;scriptor-paragraph&quot;&gt;Most DAM integrations handle the first half of this journey very well.&lt;/div&gt;
&lt;div class=&quot;scriptor-paragraph&quot;&gt;The second half is often where complexity starts to appear.&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An image may no longer belong to a target environment.&lt;/li&gt;
&lt;li&gt;An image may become hidden through metadata changes.&lt;/li&gt;
&lt;li&gt;An image may remain inside Optimizely despite no longer being available within Bynder.&lt;/li&gt;
&lt;li&gt;An image may still be actively referenced by content authors even though business rules indicate it should be removed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These scenarios introduce governance challenges that extend well beyond initial synchronization.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;&lt;strong&gt;Orphaned Assets&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;One scenario that stood out during evaluation was the possibility of orphaned assets.&lt;/p&gt;
&lt;p&gt;Consider the following sequence:&lt;/p&gt;
&lt;div class=&quot;___77lcry0 f10pi13n&quot;&gt;
&lt;div class=&quot;___1hewfwb f10pi13n f7oukh6 f5p0z4x&quot;&gt;
&lt;div class=&quot;fui-FluentProvider codeBlock-6816fui-FluentProviderr6b8 ___1jk74h0 fzk5nv6 f6n0el9 f7x6x6c f1a9mlr7 flj889f f13g0yyb ffzn0d2&quot;&gt;
&lt;div class=&quot;scriptor-component-code-block NhtKn7PZLzk24rduw4FJVQ== scriptor-codeblock-virtualized&quot;&gt;
&lt;div class=&quot;UkrLoQNicnNa5hsNIz3QLw== SpjJXGAclwYtfY+SokpSTg== ZF-Q2MvOY8aWSpGWW7iKRQ== anUhzybDvd7+8CBdKK7ukg==&quot;&gt;
&lt;div class=&quot;AHKpBuU8t+rAbDNImLlOHQ== lHY5JJVFnU8S-jx-OGwJkg== IUtI8YkF5Dl2LTlcjMDTwg==&quot;&gt;
&lt;div class=&quot;___149cboo fssjx5q f11d9hfk fj2zk3l fmyxm6j f1ldmben f1rkeeuc&quot;&gt;
&lt;div class=&quot;___svqwvq0 fssjx5q f11d9hfk fj2zk3l f1ldmben fzdgvog f19k5jia fzv833z f1r8x5cv f1687457 fe71wc6 f1wco4xj f103jgab f12fk6ok f4j985t fv86erk f1b4dd42 f1qwtq3x f13gvrpj f132if0v f1vblqk2 fkxsc7s f18j3zqo f1386a7m f1w3nze9 f14pe09c f4skf1h f1lq0dgc f1jgwha3 fbf5vau fc8703c&quot;&gt;
&lt;div class=&quot;___ysqij10 fonrc96 f1d3un5n fdk4ulh f1cijgmw fzb3wgo fssjx5q f11d9hfk fee5icb fr1uqzy&quot;&gt;
&lt;div class=&quot;___sv9ola0 fj2zk3l f1f4vkoj scriptor-instance-682&quot;&gt;
&lt;div class=&quot;scriptor-canvas scriptor-styled-scrollbar focus-container&quot;&gt;
&lt;div class=&quot;scriptor-pageContainer&quot;&gt;
&lt;div class=&quot;scriptor-pageFrame scriptor-firstPage scriptor-webView scriptor-content-visibility-auto&quot;&gt;
&lt;div class=&quot;scriptor-code-editor scriptor-code-wrap-on scriptor-pageBody scriptor-webViewPage&quot;&gt;
&lt;div class=&quot;scriptor-paragraph&quot;&gt;&lt;strong&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Asset synchronized&amp;nbsp; &amp;gt;&amp;nbsp; &lt;/span&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Metadata changes&amp;nbsp; &amp;gt;&amp;nbsp; &lt;/span&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Asset moves out of scope&amp;nbsp; &amp;gt;&amp;nbsp; &lt;/span&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Synchronization account &lt;/span&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;can no longer access asset&amp;nbsp; &amp;gt;&amp;nbsp; &lt;/span&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Asset remains in Optimizely&lt;/span&gt;&lt;/strong&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;From the perspective of the synchronization process, the asset has effectively disappeared.&lt;/p&gt;
&lt;p&gt;The integration can no longer retrieve information about the asset.&lt;/p&gt;
&lt;p&gt;However, the existing record may continue to exist inside Optimizely.&lt;/p&gt;
&lt;p&gt;Over time, these records could accumulate, creating a divergence between the source DAM and the target CMS repository.&lt;/p&gt;
&lt;p&gt;While this may not create an immediate problem, organizations with large asset libraries may eventually start asking:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Which assets are still valid?&lt;/li&gt;
&lt;li&gt;Which assets should still exist?&lt;/li&gt;
&lt;li&gt;Which assets are no longer governed by the current synchronization rules?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These questions become increasingly important as repositories grow.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;&lt;strong&gt;Environment Governance&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Another consideration involves environment ownership.&lt;/p&gt;
&lt;p&gt;Most enterprise implementations maintain separate environments:&lt;/p&gt;
&lt;div class=&quot;___77lcry0 f10pi13n&quot;&gt;
&lt;div class=&quot;___1hewfwb f10pi13n f7oukh6 f5p0z4x&quot;&gt;
&lt;div class=&quot;fui-FluentProvider codeBlock-1565fui-FluentProviderr6bh ___1jk74h0 fzk5nv6 f6n0el9 f7x6x6c f1a9mlr7 flj889f f13g0yyb ffzn0d2&quot;&gt;
&lt;div class=&quot;scriptor-component-code-block NhtKn7PZLzk24rduw4FJVQ== scriptor-codeblock-virtualized&quot;&gt;
&lt;div class=&quot;UkrLoQNicnNa5hsNIz3QLw== SpjJXGAclwYtfY+SokpSTg== ZF-Q2MvOY8aWSpGWW7iKRQ== anUhzybDvd7+8CBdKK7ukg==&quot;&gt;
&lt;div class=&quot;AHKpBuU8t+rAbDNImLlOHQ== lHY5JJVFnU8S-jx-OGwJkg== IUtI8YkF5Dl2LTlcjMDTwg==&quot;&gt;
&lt;div class=&quot;___149cboo fssjx5q f11d9hfk fj2zk3l fmyxm6j f1ldmben f1rkeeuc&quot;&gt;
&lt;div class=&quot;___svqwvq0 fssjx5q f11d9hfk fj2zk3l f1ldmben fzdgvog f19k5jia fzv833z f1r8x5cv f1687457 fe71wc6 f1wco4xj f103jgab f12fk6ok f4j985t fv86erk f1b4dd42 f1qwtq3x f13gvrpj f132if0v f1vblqk2 fkxsc7s f18j3zqo f1386a7m f1w3nze9 f14pe09c f4skf1h f1lq0dgc f1jgwha3 fbf5vau fc8703c&quot;&gt;
&lt;div class=&quot;___ysqij10 fonrc96 f1d3un5n fdk4ulh f1cijgmw fzb3wgo fssjx5q f11d9hfk fee5icb fr1uqzy&quot;&gt;
&lt;div class=&quot;___sv9ola0 fj2zk3l f1f4vkoj scriptor-instance-683&quot;&gt;
&lt;div class=&quot;scriptor-canvas scriptor-styled-scrollbar focus-container&quot;&gt;
&lt;div class=&quot;scriptor-pageContainer&quot;&gt;
&lt;div class=&quot;scriptor-pageFrame scriptor-firstPage scriptor-webView scriptor-content-visibility-auto&quot;&gt;
&lt;div class=&quot;scriptor-code-editor scriptor-code-wrap-on scriptor-pageBody scriptor-webViewPage&quot;&gt;
&lt;div class=&quot;scriptor-paragraph&quot;&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Development&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Staging&lt;/li&gt;
&lt;li&gt;Production&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;However, DAM platforms are often centrally managed.&lt;/p&gt;
&lt;p&gt;A common scenario looks something like this:&lt;/p&gt;
&lt;p&gt;&lt;img style=&quot;border-width: 1px; border-style: solid;&quot; src=&quot;/link/4c2f0e70051d44828bb2530d6be7e584.aspx&quot; alt=&quot;&quot; width=&quot;722&quot; height=&quot;482&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Without additional controls, all environments may receive identical assets.&lt;/p&gt;
&lt;p&gt;This can introduce several concerns:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Increased storage consumption&lt;/li&gt;
&lt;li&gt;Cluttered non-production environments&lt;/li&gt;
&lt;li&gt;Difficult testing scenarios&lt;/li&gt;
&lt;li&gt;Confusion around environment-specific assets&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;Suggested Environment Metadata Approach&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;One possible approach is introducing an additional metadata property or use tags inside Bynder.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;&quot;website-environment&quot; metadata property with p&lt;/span&gt;ossible values:&lt;/p&gt;
&lt;div class=&quot;___77lcry0 f10pi13n&quot;&gt;
&lt;div class=&quot;___1hewfwb f10pi13n f7oukh6 f5p0z4x&quot;&gt;
&lt;div class=&quot;fui-FluentProvider codeBlock-9217fui-FluentProviderr6cc ___1jk74h0 fzk5nv6 f6n0el9 f7x6x6c f1a9mlr7 flj889f f13g0yyb ffzn0d2&quot;&gt;
&lt;div class=&quot;scriptor-component-code-block NhtKn7PZLzk24rduw4FJVQ== scriptor-codeblock-virtualized&quot;&gt;
&lt;div class=&quot;UkrLoQNicnNa5hsNIz3QLw== SpjJXGAclwYtfY+SokpSTg== ZF-Q2MvOY8aWSpGWW7iKRQ== anUhzybDvd7+8CBdKK7ukg==&quot;&gt;
&lt;div class=&quot;AHKpBuU8t+rAbDNImLlOHQ== lHY5JJVFnU8S-jx-OGwJkg== IUtI8YkF5Dl2LTlcjMDTwg==&quot;&gt;
&lt;div class=&quot;___149cboo fssjx5q f11d9hfk fj2zk3l fmyxm6j f1ldmben f1rkeeuc&quot;&gt;
&lt;div class=&quot;___svqwvq0 fssjx5q f11d9hfk fj2zk3l f1ldmben fzdgvog f19k5jia fzv833z f1r8x5cv f1687457 fe71wc6 f1wco4xj f103jgab f12fk6ok f4j985t fv86erk f1b4dd42 f1qwtq3x f13gvrpj f132if0v f1vblqk2 fkxsc7s f18j3zqo f1386a7m f1w3nze9 f14pe09c f4skf1h f1lq0dgc f1jgwha3 fbf5vau fc8703c&quot;&gt;
&lt;div class=&quot;___ysqij10 fonrc96 f1d3un5n fdk4ulh f1cijgmw fzb3wgo fssjx5q f11d9hfk fee5icb fr1uqzy&quot;&gt;
&lt;div class=&quot;___sv9ola0 fj2zk3l f1f4vkoj scriptor-instance-686&quot;&gt;
&lt;div class=&quot;scriptor-canvas scriptor-styled-scrollbar focus-container&quot;&gt;
&lt;div class=&quot;scriptor-pageContainer&quot;&gt;
&lt;div class=&quot;scriptor-pageFrame scriptor-firstPage scriptor-webView scriptor-content-visibility-auto&quot;&gt;
&lt;div class=&quot;scriptor-code-editor scriptor-code-wrap-on scriptor-pageBody scriptor-webViewPage&quot;&gt;
&lt;ul&gt;
&lt;li class=&quot;scriptor-paragraph&quot;&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Development&lt;/span&gt;&lt;/li&gt;
&lt;li class=&quot;scriptor-paragraph&quot;&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Staging&lt;/span&gt;&lt;/li&gt;
&lt;li class=&quot;scriptor-paragraph&quot;&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Production&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This metadata could travel alongside normal asset synchronization and provide additional governance information to downstream systems.&lt;/p&gt;
&lt;h4&gt;&lt;strong&gt;Suggested Visibility Configuration&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Unlike visibility-based filtering rules, this property/tags does not need to setup separate profiles/users and any explicit filtering rules.&lt;/p&gt;
&lt;p&gt;The goal is not to hide assets. The goal is to communicate intent.&lt;/p&gt;
&lt;div class=&quot;___77lcry0 f10pi13n&quot;&gt;
&lt;div class=&quot;___1hewfwb f10pi13n f7oukh6 f5p0z4x&quot;&gt;
&lt;div class=&quot;fui-FluentProvider codeBlock-2862fui-FluentProviderr6cu ___1jk74h0 fzk5nv6 f6n0el9 f7x6x6c f1a9mlr7 flj889f f13g0yyb ffzn0d2&quot;&gt;
&lt;div class=&quot;scriptor-component-code-block NhtKn7PZLzk24rduw4FJVQ== scriptor-codeblock-virtualized&quot;&gt;
&lt;div class=&quot;UkrLoQNicnNa5hsNIz3QLw== SpjJXGAclwYtfY+SokpSTg== ZF-Q2MvOY8aWSpGWW7iKRQ== anUhzybDvd7+8CBdKK7ukg==&quot;&gt;
&lt;div class=&quot;AHKpBuU8t+rAbDNImLlOHQ== lHY5JJVFnU8S-jx-OGwJkg== IUtI8YkF5Dl2LTlcjMDTwg==&quot;&gt;
&lt;div class=&quot;___149cboo fssjx5q f11d9hfk fj2zk3l fmyxm6j f1ldmben f1rkeeuc&quot;&gt;
&lt;div class=&quot;___svqwvq0 fssjx5q f11d9hfk fj2zk3l f1ldmben fzdgvog f19k5jia fzv833z f1r8x5cv f1687457 fe71wc6 f1wco4xj f103jgab f12fk6ok f4j985t fv86erk f1b4dd42 f1qwtq3x f13gvrpj f132if0v f1vblqk2 fkxsc7s f18j3zqo f1386a7m f1w3nze9 f14pe09c f4skf1h f1lq0dgc f1jgwha3 fbf5vau fc8703c&quot;&gt;
&lt;div class=&quot;___ysqij10 fonrc96 f1d3un5n fdk4ulh f1cijgmw fzb3wgo fssjx5q f11d9hfk fee5icb fr1uqzy&quot;&gt;
&lt;div class=&quot;___sv9ola0 fj2zk3l f1f4vkoj scriptor-instance-688&quot;&gt;
&lt;div class=&quot;scriptor-canvas scriptor-styled-scrollbar focus-container&quot;&gt;
&lt;div class=&quot;scriptor-pageContainer&quot;&gt;
&lt;div class=&quot;scriptor-pageFrame scriptor-firstPage scriptor-webView scriptor-content-visibility-auto&quot;&gt;
&lt;div class=&quot;scriptor-code-editor scriptor-code-wrap-on scriptor-pageBody scriptor-webViewPage&quot;&gt;
&lt;div class=&quot;scriptor-paragraph&quot;&gt;For example: If an asset that is tagged for development environment received in production environement then that asset should be treated as out of scope and vice versa for other environments.&lt;/div&gt;
&lt;div class=&quot;scriptor-paragraph&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;scriptor-paragraph&quot;&gt;Asset target Environment: Development&lt;/div&gt;
&lt;div class=&quot;scriptor-paragraph&quot;&gt;Asset Synced in: Production&lt;/div&gt;
&lt;div class=&quot;scriptor-paragraph&quot;&gt;Result: Potentially Out of Scope&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;scriptor-highlightWrapper&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;scriptor-paragraph&quot;&gt;
&lt;div&gt;With this approach, a centralized DAM repository can continue serving multiple environments while still supporting environment-specific governance decisions.&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr /&gt;
&lt;h2&gt;&lt;strong&gt;Why Environment Metadata May Be Valuable&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Without environment metadata, every environment receives same assets.&lt;/p&gt;
&lt;p&gt;With environment metadata, environment validation processes can be setup to stop storing assets those are not required in the environment.&lt;/p&gt;
&lt;p&gt;This approach may provide a lightweight governance layer without introducing separate DAM repositories, separate taxonomies, or separate synchronization configurations.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;&lt;strong&gt;Architectural Note&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;The workflow described in the following sections represents an architectural approach that emerged during the evaluation of lifecycle management challenges associated with large-scale DAM integrations.&lt;/p&gt;
&lt;p&gt;While the individual platform capabilities required for this model&amp;mdash;including Bynder webhooks, permanent access tokens, Optimizely Graph/Content Management APIs, and Optimizely Graph reference queries&amp;mdash; are available today, the complete end-to-end workflow has not yet been fully validated in a production implementation.&lt;/p&gt;
&lt;p&gt;The intent of this article is to explore a potential governance pattern and discuss its trade-offs rather than present a proven production architecture.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;&lt;strong&gt;Exploring an Event-Driven Governance Model&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;One approach that appears interesting from an architectural perspective is introducing a webhook-driven governance workflow.&lt;/p&gt;
&lt;p&gt;For simplicity, this article assumes the webhook endpoint is deployed inside the frontend nextjs application.&lt;/p&gt;
&lt;p&gt;In practice, the webhook could be hosted anywhere:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Next.js API Route&lt;/li&gt;
&lt;li&gt;Azure Function&lt;/li&gt;
&lt;li&gt;AWS Lambda&lt;/li&gt;
&lt;li&gt;Google Cloud Function&lt;/li&gt;
&lt;li&gt;Dedicated Middleware Service&lt;/li&gt;
&lt;li&gt;Integration Platform&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The location itself is less important than the validation process being executed.&lt;/p&gt;
&lt;p&gt;At a high level:&lt;/p&gt;
&lt;div class=&quot;___77lcry0 f10pi13n&quot;&gt;
&lt;div class=&quot;___1hewfwb f10pi13n f7oukh6 f5p0z4x&quot;&gt;
&lt;div class=&quot;fui-FluentProvider codeBlock-1675fui-FluentProviderr6dp ___1jk74h0 fzk5nv6 f6n0el9 f7x6x6c f1a9mlr7 flj889f f13g0yyb ffzn0d2&quot;&gt;
&lt;div class=&quot;scriptor-component-code-block NhtKn7PZLzk24rduw4FJVQ== scriptor-codeblock-virtualized&quot;&gt;
&lt;div class=&quot;UkrLoQNicnNa5hsNIz3QLw== SpjJXGAclwYtfY+SokpSTg== ZF-Q2MvOY8aWSpGWW7iKRQ== anUhzybDvd7+8CBdKK7ukg==&quot;&gt;
&lt;div class=&quot;AHKpBuU8t+rAbDNImLlOHQ== lHY5JJVFnU8S-jx-OGwJkg== IUtI8YkF5Dl2LTlcjMDTwg==&quot;&gt;
&lt;div class=&quot;___149cboo fssjx5q f11d9hfk fj2zk3l fmyxm6j f1ldmben f1rkeeuc&quot;&gt;
&lt;div class=&quot;___svqwvq0 fssjx5q f11d9hfk fj2zk3l f1ldmben fzdgvog f19k5jia fzv833z f1r8x5cv f1687457 fe71wc6 f1wco4xj f103jgab f12fk6ok f4j985t fv86erk f1b4dd42 f1qwtq3x f13gvrpj f132if0v f1vblqk2 fkxsc7s f18j3zqo f1386a7m f1w3nze9 f14pe09c f4skf1h f1lq0dgc f1jgwha3 fbf5vau fc8703c&quot;&gt;
&lt;div class=&quot;___ysqij10 fonrc96 f1d3un5n fdk4ulh f1cijgmw fzb3wgo fssjx5q f11d9hfk fee5icb fr1uqzy&quot;&gt;
&lt;div class=&quot;___sv9ola0 fj2zk3l f1f4vkoj scriptor-instance-691&quot;&gt;
&lt;div class=&quot;scriptor-canvas scriptor-styled-scrollbar focus-container&quot;&gt;
&lt;div class=&quot;scriptor-pageContainer&quot;&gt;
&lt;div class=&quot;scriptor-pageFrame scriptor-firstPage scriptor-webView scriptor-content-visibility-auto&quot;&gt;
&lt;div class=&quot;scriptor-code-editor scriptor-code-wrap-on scriptor-pageBody scriptor-webViewPage&quot;&gt;
&lt;div class=&quot;scriptor-paragraph&quot;&gt;&lt;strong&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Bynder Calls Webhook&amp;nbsp; &amp;gt;&amp;nbsp; &lt;/span&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Webhook Receives event&amp;nbsp; &amp;gt;&amp;nbsp; &lt;/span&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Validation Logic&amp;nbsp; &amp;gt;&amp;nbsp; Asset remains or Deleted&lt;/span&gt;&lt;/strong&gt;&lt;/div&gt;
&lt;div class=&quot;scriptor-paragraph&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;scriptor-paragraph&quot;&gt;&lt;strong&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;&lt;img style=&quot;border-width: 1px; border-style: solid;&quot; src=&quot;/link/6924927432474ebbbf0173898da1d109.aspx?1783236197175&quot; width=&quot;575&quot; height=&quot;699&quot; /&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;___77lcry0 f10pi13n&quot;&gt;
&lt;div class=&quot;___1hewfwb f10pi13n f7oukh6 f5p0z4x&quot;&gt;
&lt;div class=&quot;fui-FluentProvider codeBlock-1955fui-FluentProviderr6e2 ___1jk74h0 fzk5nv6 f6n0el9 f7x6x6c f1a9mlr7 flj889f f13g0yyb ffzn0d2&quot;&gt;
&lt;div class=&quot;scriptor-component-code-block NhtKn7PZLzk24rduw4FJVQ== scriptor-codeblock-virtualized&quot;&gt;
&lt;div class=&quot;UkrLoQNicnNa5hsNIz3QLw== SpjJXGAclwYtfY+SokpSTg== ZF-Q2MvOY8aWSpGWW7iKRQ== anUhzybDvd7+8CBdKK7ukg==&quot;&gt;
&lt;div class=&quot;AHKpBuU8t+rAbDNImLlOHQ== lHY5JJVFnU8S-jx-OGwJkg== IUtI8YkF5Dl2LTlcjMDTwg==&quot;&gt;
&lt;div class=&quot;___149cboo fssjx5q f11d9hfk fj2zk3l fmyxm6j f1ldmben f1rkeeuc&quot;&gt;
&lt;div class=&quot;___svqwvq0 fssjx5q f11d9hfk fj2zk3l f1ldmben fzdgvog f19k5jia fzv833z f1r8x5cv f1687457 fe71wc6 f1wco4xj f103jgab f12fk6ok f4j985t fv86erk f1b4dd42 f1qwtq3x f13gvrpj f132if0v f1vblqk2 fkxsc7s f18j3zqo f1386a7m f1w3nze9 f14pe09c f4skf1h f1lq0dgc f1jgwha3 fbf5vau fc8703c&quot;&gt;
&lt;div class=&quot;___ysqij10 fonrc96 f1d3un5n fdk4ulh f1cijgmw fzb3wgo fssjx5q f11d9hfk fee5icb fr1uqzy&quot;&gt;
&lt;div class=&quot;___sv9ola0 fj2zk3l f1f4vkoj scriptor-instance-692&quot;&gt;
&lt;div class=&quot;scriptor-canvas scriptor-styled-scrollbar focus-container&quot;&gt;
&lt;div class=&quot;scriptor-pageContainer&quot;&gt;
&lt;div class=&quot;scriptor-pageFrame scriptor-firstPage scriptor-webView scriptor-content-visibility-auto&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;scriptor-code-scroll-proxy&quot;&gt;
&lt;h2&gt;&lt;strong&gt;Three Gates&lt;/strong&gt;&lt;/h2&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3&gt;&lt;strong&gt;Gate 1: Visibility Validation&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;The first validation stage could determine whether the asset remains visible to the synchronization account.&lt;br /&gt;Note: Use permanent token assigned for the synchronization account.&lt;/p&gt;
&lt;p&gt;Conceptually:&lt;/p&gt;
&lt;div class=&quot;___77lcry0 f10pi13n&quot;&gt;
&lt;div class=&quot;___1hewfwb f10pi13n f7oukh6 f5p0z4x&quot;&gt;
&lt;div class=&quot;fui-FluentProvider codeBlock-2459fui-FluentProviderr6eb ___1jk74h0 fzk5nv6 f6n0el9 f7x6x6c f1a9mlr7 flj889f f13g0yyb ffzn0d2&quot;&gt;
&lt;div class=&quot;scriptor-component-code-block NhtKn7PZLzk24rduw4FJVQ== scriptor-codeblock-virtualized&quot;&gt;
&lt;div class=&quot;UkrLoQNicnNa5hsNIz3QLw== SpjJXGAclwYtfY+SokpSTg== ZF-Q2MvOY8aWSpGWW7iKRQ== anUhzybDvd7+8CBdKK7ukg==&quot;&gt;
&lt;div class=&quot;AHKpBuU8t+rAbDNImLlOHQ== lHY5JJVFnU8S-jx-OGwJkg== IUtI8YkF5Dl2LTlcjMDTwg==&quot;&gt;
&lt;div class=&quot;___149cboo fssjx5q f11d9hfk fj2zk3l fmyxm6j f1ldmben f1rkeeuc&quot;&gt;
&lt;div class=&quot;___svqwvq0 fssjx5q f11d9hfk fj2zk3l f1ldmben fzdgvog f19k5jia fzv833z f1r8x5cv f1687457 fe71wc6 f1wco4xj f103jgab f12fk6ok f4j985t fv86erk f1b4dd42 f1qwtq3x f13gvrpj f132if0v f1vblqk2 fkxsc7s f18j3zqo f1386a7m f1w3nze9 f14pe09c f4skf1h f1lq0dgc f1jgwha3 fbf5vau fc8703c&quot;&gt;
&lt;div class=&quot;___ysqij10 fonrc96 f1d3un5n fdk4ulh f1cijgmw fzb3wgo fssjx5q f11d9hfk fee5icb fr1uqzy&quot;&gt;
&lt;div class=&quot;___sv9ola0 fj2zk3l f1f4vkoj scriptor-instance-693&quot;&gt;
&lt;div class=&quot;scriptor-canvas scriptor-styled-scrollbar focus-container&quot;&gt;
&lt;div class=&quot;scriptor-pageContainer&quot;&gt;
&lt;div class=&quot;scriptor-pageFrame scriptor-firstPage scriptor-webView scriptor-content-visibility-auto&quot;&gt;
&lt;div class=&quot;scriptor-code-editor scriptor-code-wrap-on scriptor-pageBody scriptor-webViewPage&quot;&gt;
&lt;div class=&quot;scriptor-paragraph&quot;&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Asset Updated&amp;nbsp; &amp;gt;&amp;nbsp; &lt;/span&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Retrieve Asset&amp;nbsp; &amp;gt;&amp;nbsp; &lt;/span&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Accessible&amp;nbsp; &amp;gt;&amp;nbsp; &lt;/span&gt;Decision&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;___77lcry0 f10pi13n&quot;&gt;
&lt;div class=&quot;___1hewfwb f10pi13n f7oukh6 f5p0z4x&quot;&gt;
&lt;div class=&quot;fui-FluentProvider codeBlock-5518fui-FluentProviderr6ek ___1jk74h0 fzk5nv6 f6n0el9 f7x6x6c f1a9mlr7 flj889f f13g0yyb ffzn0d2&quot;&gt;
&lt;div class=&quot;scriptor-component-code-block NhtKn7PZLzk24rduw4FJVQ== scriptor-codeblock-virtualized&quot;&gt;
&lt;div class=&quot;UkrLoQNicnNa5hsNIz3QLw== SpjJXGAclwYtfY+SokpSTg== ZF-Q2MvOY8aWSpGWW7iKRQ== anUhzybDvd7+8CBdKK7ukg==&quot;&gt;
&lt;div class=&quot;AHKpBuU8t+rAbDNImLlOHQ== lHY5JJVFnU8S-jx-OGwJkg== IUtI8YkF5Dl2LTlcjMDTwg==&quot;&gt;
&lt;div class=&quot;___149cboo fssjx5q f11d9hfk fj2zk3l fmyxm6j f1ldmben f1rkeeuc&quot;&gt;
&lt;div class=&quot;___svqwvq0 fssjx5q f11d9hfk fj2zk3l f1ldmben fzdgvog f19k5jia fzv833z f1r8x5cv f1687457 fe71wc6 f1wco4xj f103jgab f12fk6ok f4j985t fv86erk f1b4dd42 f1qwtq3x f13gvrpj f132if0v f1vblqk2 fkxsc7s f18j3zqo f1386a7m f1w3nze9 f14pe09c f4skf1h f1lq0dgc f1jgwha3 fbf5vau fc8703c&quot;&gt;
&lt;div class=&quot;___ysqij10 fonrc96 f1d3un5n fdk4ulh f1cijgmw fzb3wgo fssjx5q f11d9hfk fee5icb fr1uqzy&quot;&gt;
&lt;div class=&quot;___sv9ola0 fj2zk3l f1f4vkoj scriptor-instance-694&quot;&gt;
&lt;div class=&quot;scriptor-canvas scriptor-styled-scrollbar focus-container&quot;&gt;
&lt;div class=&quot;scriptor-pageContainer&quot;&gt;
&lt;div class=&quot;scriptor-pageFrame scriptor-firstPage scriptor-webView scriptor-content-visibility-auto&quot;&gt;
&lt;div class=&quot;scriptor-code-editor scriptor-code-wrap-on scriptor-pageBody scriptor-webViewPage&quot;&gt;
&lt;div class=&quot;scriptor-paragraph&quot;&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Decision&amp;nbsp; &amp;gt;&amp;nbsp; YES&amp;nbsp; &amp;gt;&amp;nbsp; &lt;/span&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Continue&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;scriptor-paragraph&quot;&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Decision&amp;nbsp; &amp;gt;&amp;nbsp; NO&amp;nbsp; &amp;gt;&amp;nbsp; &lt;/span&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Potential Cleanup Candidate&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The objective is not necessarily to delete the asset immediately. Instead, the objective is to determine whether the asset still falls within the intended synchronization scope. If visibility has changed, additional evaluation may be required.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;&lt;strong&gt;Gate 2: Environment Validation&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;The second validation stage could evaluate environmental ownership.&lt;/p&gt;
&lt;p&gt;Example workflow:&lt;/p&gt;
&lt;div class=&quot;___77lcry0 f10pi13n&quot;&gt;
&lt;div class=&quot;___1hewfwb f10pi13n f7oukh6 f5p0z4x&quot;&gt;
&lt;div class=&quot;fui-FluentProvider codeBlock-5562fui-FluentProviderr6et ___1jk74h0 fzk5nv6 f6n0el9 f7x6x6c f1a9mlr7 flj889f f13g0yyb ffzn0d2&quot;&gt;
&lt;div class=&quot;scriptor-component-code-block NhtKn7PZLzk24rduw4FJVQ== scriptor-codeblock-virtualized&quot;&gt;
&lt;div class=&quot;UkrLoQNicnNa5hsNIz3QLw== SpjJXGAclwYtfY+SokpSTg== ZF-Q2MvOY8aWSpGWW7iKRQ== anUhzybDvd7+8CBdKK7ukg==&quot;&gt;
&lt;div class=&quot;AHKpBuU8t+rAbDNImLlOHQ== lHY5JJVFnU8S-jx-OGwJkg== IUtI8YkF5Dl2LTlcjMDTwg==&quot;&gt;
&lt;div class=&quot;___149cboo fssjx5q f11d9hfk fj2zk3l fmyxm6j f1ldmben f1rkeeuc&quot;&gt;
&lt;div class=&quot;___svqwvq0 fssjx5q f11d9hfk fj2zk3l f1ldmben fzdgvog f19k5jia fzv833z f1r8x5cv f1687457 fe71wc6 f1wco4xj f103jgab f12fk6ok f4j985t fv86erk f1b4dd42 f1qwtq3x f13gvrpj f132if0v f1vblqk2 fkxsc7s f18j3zqo f1386a7m f1w3nze9 f14pe09c f4skf1h f1lq0dgc f1jgwha3 fbf5vau fc8703c&quot;&gt;
&lt;div class=&quot;___ysqij10 fonrc96 f1d3un5n fdk4ulh f1cijgmw fzb3wgo fssjx5q f11d9hfk fee5icb fr1uqzy&quot;&gt;
&lt;div class=&quot;___sv9ola0 fj2zk3l f1f4vkoj scriptor-instance-695&quot;&gt;
&lt;div class=&quot;scriptor-canvas scriptor-styled-scrollbar focus-container&quot;&gt;
&lt;div class=&quot;scriptor-pageContainer&quot;&gt;
&lt;div class=&quot;scriptor-pageFrame scriptor-firstPage scriptor-webView scriptor-content-visibility-auto&quot;&gt;
&lt;div class=&quot;scriptor-code-editor scriptor-code-wrap-on scriptor-pageBody scriptor-webViewPage&quot;&gt;
&lt;div class=&quot;scriptor-paragraph&quot;&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Asset Updated&amp;nbsp; &amp;gt; &lt;/span&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Read website-environment&amp;nbsp; &amp;gt;&amp;nbsp; &lt;/span&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Compare Against &lt;/span&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Current Environment&amp;nbsp; &amp;gt; &lt;/span&gt;Decision:&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;___77lcry0 f10pi13n&quot;&gt;
&lt;div class=&quot;___1hewfwb f10pi13n f7oukh6 f5p0z4x&quot;&gt;
&lt;div class=&quot;fui-FluentProvider codeBlock-2919fui-FluentProviderr6f6 ___1jk74h0 fzk5nv6 f6n0el9 f7x6x6c f1a9mlr7 flj889f f13g0yyb ffzn0d2&quot;&gt;
&lt;div class=&quot;scriptor-component-code-block NhtKn7PZLzk24rduw4FJVQ== scriptor-codeblock-virtualized&quot;&gt;
&lt;div class=&quot;CDXiecnD7ekDByb7Z+4oEA==&quot;&gt;
&lt;div class=&quot;r14iuplo fui-AriaLive__assertive&quot;&gt;
&lt;div class=&quot;scriptor-paragraph&quot;&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Decision&amp;nbsp; &amp;gt;&amp;nbsp; Match&amp;nbsp; &amp;gt;&amp;nbsp; &lt;/span&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Continue&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;scriptor-paragraph&quot;&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Decision&amp;nbsp; &amp;gt;&amp;nbsp; No Match&amp;nbsp; &amp;gt;&amp;nbsp; &lt;/span&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Potential Cleanup Candidate&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This allows governance decisions to be made without requiring separate DAM repositories.&lt;/p&gt;
&lt;p&gt;An asset intended exclusively for Production could potentially be excluded from lower environments while remaining available in Production.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;&lt;strong&gt;Gate 3: Reference Validation&lt;/strong&gt;&lt;/h3&gt;
&lt;div class=&quot;_1l4j4na3 _667h42&quot;&gt;Before any removal occurs, a critical question must be answered: &lt;strong class=&quot;far4bl _1a0p899 _667h42&quot;&gt;Is the asset still being used?&lt;/strong&gt;&lt;/div&gt;
&lt;div class=&quot;_1l4j4na3 _667h42&quot;&gt;Optimizely Graph does not currently expose a built-in reverse-reference lookup operator. This means there is no single, generic query to answer &quot;which content items reference this asset?&quot; without knowledge of the content model.&lt;/div&gt;
&lt;div class=&quot;_1l4j4na3 _667h42&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;_1l4j4na3 _667h42&quot;&gt;&lt;strong class=&quot;far4bl _1a0p899 _667h42&quot;&gt;Possible implementation approaches include:&lt;/strong&gt;&lt;/div&gt;
&lt;ol class=&quot;_667h42 _1l4j4na7&quot;&gt;
&lt;li class=&quot;_667h42 _1l4j4na2&quot;&gt;
&lt;div class=&quot;_1l4j4na3 _667h42&quot;&gt;&lt;strong class=&quot;far4bl _1a0p899 _667h42&quot;&gt;Full-text search&lt;/strong&gt;&amp;nbsp;&amp;mdash; Query&amp;nbsp;&lt;code class=&quot;_667h42 _1l4j4na5&quot;&gt;Content&lt;/code&gt;&amp;nbsp;using&amp;nbsp;&lt;code class=&quot;_667h42 _1l4j4na5&quot;&gt;_fulltext: { match: &quot;asset-id&quot; }&lt;/code&gt;&amp;nbsp;to search across all indexed content. Quick but imprecise &amp;mdash; depends on how asset IDs are indexed.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;_667h42 _1l4j4na2&quot;&gt;
&lt;div class=&quot;_1l4j4na3 _667h42&quot;&gt;&lt;strong class=&quot;far4bl _1a0p899 _667h42&quot;&gt;Schema introspection + targeted queries&lt;/strong&gt;&amp;nbsp;&amp;mdash; Use GraphQL introspection to discover all content types with reference-type fields, then query each one. More thorough but requires automation to be practical.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;_667h42 _1l4j4na2&quot;&gt;
&lt;div class=&quot;_1l4j4na3 _667h42&quot;&gt;&lt;strong class=&quot;far4bl _1a0p899 _667h42&quot;&gt;Content Management API enumeration&lt;/strong&gt;&amp;nbsp;&amp;mdash; Use the CMS REST API to walk content and check references programmatically.&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;_1l4j4na3 _667h42&quot;&gt;The right approach depends on your content model complexity, performance requirements, and how frequently this validation runs.&amp;nbsp;&lt;strong class=&quot;far4bl _1a0p899 _667h42&quot;&gt;This gate will likely require a custom implementation tailored to your environment.&lt;/strong&gt;&lt;/div&gt;
&lt;div class=&quot;_1l4j4na3 _667h42&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;hr /&gt;
&lt;h3&gt;&lt;strong&gt;Deleting Assets from Optimizely&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Once an asset has been evaluated, a governance process may decide that removal should occur.&lt;/p&gt;
&lt;p&gt;Important questions include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Is the asset still visible?&lt;/li&gt;
&lt;li&gt;Is the asset assigned to the current environment?&lt;/li&gt;
&lt;li&gt;Is the asset actively referenced?&lt;/li&gt;
&lt;li&gt;Should the asset remain synchronized?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If all validation checks indicate removal is appropriate, a deletion request could be submitted through the Content Management API.&lt;/p&gt;
&lt;p&gt;An important distinction exists between: &lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Soft Delete vs &lt;/span&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Hard Delete&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;For lifecycle-management scenarios, hard deletion may be more relevant because the objective is often to reduce repository footprint rather than move content into a recycle state.&lt;/p&gt;
&lt;div class=&quot;___77lcry0 f10pi13n&quot;&gt;
&lt;div class=&quot;___1hewfwb f10pi13n f7oukh6 f5p0z4x&quot;&gt;
&lt;div class=&quot;fui-FluentProvider codeBlock-9961fui-FluentProviderr6gs ___1jk74h0 fzk5nv6 f6n0el9 f7x6x6c f1a9mlr7 flj889f f13g0yyb ffzn0d2&quot;&gt;
&lt;div class=&quot;scriptor-component-code-block NhtKn7PZLzk24rduw4FJVQ== scriptor-codeblock-virtualized&quot;&gt;
&lt;div class=&quot;CDXiecnD7ekDByb7Z+4oEA==&quot;&gt;
&lt;div class=&quot;r14iuplo fui-AriaLive__assertive&quot;&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100.013%; margin-left: 0px; border-width: 1px; border-style: solid; margin-right: auto;&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 10.9517%; text-align: left; vertical-align: top; border-style: solid; border-width: 1px;&quot;&gt;&lt;strong&gt;Url &amp;amp; Headers&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&quot;width: 89.0357%; text-align: left; vertical-align: top; border-style: solid; border-width: 1px;&quot;&gt;
&lt;div class=&quot;_47QlA7XYWbfK2INfVvTUjg==&quot;&gt;
&lt;div class=&quot;iVmNpJv5Hu1Eu0ev0L3Ffg==&quot;&gt;
&lt;div class=&quot;KU8Z3uOm5OU11Ica53+OVA==&quot;&gt;
&lt;div class=&quot;fui-Overflow Ye6tJij6H1xMCTdS6F25rA== ___s5d2bt0 f3x039c facid5&quot;&gt;
&lt;div class=&quot;vCHKFE0-MqMN20DBXhx6Ow==&quot;&gt;
&lt;div class=&quot;fui-Badge r1p3wql8 _9liocQIfHYrfQrOa5IZ50A== ___engtnk0 f1soahph fyk0x7v f17ad7pc f1evqen2 f70w86f fmfhf0j f1ar0y7c f1fzzbuc f176ju0o&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
POST /api/content/v2/data?id=bynd HTTP/1.1&lt;br /&gt;Host: cg.optimizely.com&lt;br /&gt;Content-Type: application/x-ndjson&lt;br /&gt;Authorization: Basic V25KQmNtUlpSNTFYaDJuSmE1UWV5eEkwWW...[Your_Base64_Encoded_Credentials]&lt;br /&gt;
&lt;div class=&quot;UkrLoQNicnNa5hsNIz3QLw== SpjJXGAclwYtfY+SokpSTg== ZF-Q2MvOY8aWSpGWW7iKRQ== anUhzybDvd7+8CBdKK7ukg==&quot;&gt;
&lt;div class=&quot;AHKpBuU8t+rAbDNImLlOHQ== lHY5JJVFnU8S-jx-OGwJkg== IUtI8YkF5Dl2LTlcjMDTwg==&quot;&gt;
&lt;div class=&quot;___149cboo fssjx5q f11d9hfk fj2zk3l fmyxm6j f1ldmben f1rkeeuc&quot;&gt;
&lt;div class=&quot;___svqwvq0 fssjx5q f11d9hfk fj2zk3l f1ldmben fzdgvog f19k5jia fzv833z f1r8x5cv f1687457 fe71wc6 f1wco4xj f103jgab f12fk6ok f4j985t fv86erk f1b4dd42 f1qwtq3x f13gvrpj f132if0v f1vblqk2 fkxsc7s f18j3zqo f1386a7m f1w3nze9 f14pe09c f4skf1h f1lq0dgc f1jgwha3 fbf5vau fc8703c&quot;&gt;
&lt;div class=&quot;___ysqij10 fonrc96 f1d3un5n fdk4ulh f1cijgmw fzb3wgo fssjx5q f11d9hfk fee5icb fr1uqzy&quot;&gt;
&lt;div class=&quot;___sv9ola0 fj2zk3l f1f4vkoj scriptor-instance-702&quot;&gt;
&lt;div class=&quot;scriptor-canvas scriptor-styled-scrollbar focus-container&quot;&gt;
&lt;div class=&quot;scriptor-pageContainer&quot;&gt;
&lt;div class=&quot;scriptor-pageFrame scriptor-firstPage scriptor-webView scriptor-content-visibility-auto&quot;&gt;
&lt;div class=&quot;scriptor-code-editor scriptor-code-wrap-on scriptor-pageBody scriptor-webViewPage&quot;&gt;
&lt;div class=&quot;scriptor-paragraph&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 10.9517%; text-align: left; vertical-align: top; border-style: solid; border-width: 1px;&quot;&gt;&lt;strong&gt;Body&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&quot;width: 89.0357%; text-align: left; vertical-align: top; border-style: solid; border-width: 1px;&quot;&gt;{&quot;delete&quot;:{&quot;_id&quot;:&quot;bynder-asset-guid-12345&quot;,&quot;language_routing&quot;:&quot;en&quot;}}&lt;br /&gt;{}&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 10.9517%; text-align: left; vertical-align: top; border-style: solid; border-width: 1px;&quot;&gt;&lt;strong&gt;Response&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&quot;width: 89.0357%; text-align: left; vertical-align: top; border-style: solid; border-width: 1px;&quot;&gt;{&lt;br /&gt;&amp;nbsp; &quot;status&quot;: &quot;success&quot;,&lt;br /&gt;&amp;nbsp; &quot;total_records_processed&quot;: 1,&lt;br /&gt;&amp;nbsp; &quot;deleted&quot;: 1,&lt;br /&gt;&amp;nbsp; &quot;errors&quot;: [],&lt;br /&gt;&amp;nbsp; &quot;took&quot;: 42&lt;br /&gt;}&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;_47QlA7XYWbfK2INfVvTUjg==&quot;&gt;
&lt;div class=&quot;iVmNpJv5Hu1Eu0ev0L3Ffg==&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;UkrLoQNicnNa5hsNIz3QLw== SpjJXGAclwYtfY+SokpSTg== ZF-Q2MvOY8aWSpGWW7iKRQ== anUhzybDvd7+8CBdKK7ukg==&quot;&gt;
&lt;div class=&quot;AHKpBuU8t+rAbDNImLlOHQ== lHY5JJVFnU8S-jx-OGwJkg== IUtI8YkF5Dl2LTlcjMDTwg==&quot;&gt;
&lt;div class=&quot;___149cboo fssjx5q f11d9hfk fj2zk3l fmyxm6j f1ldmben f1rkeeuc&quot;&gt;
&lt;div class=&quot;___svqwvq0 fssjx5q f11d9hfk fj2zk3l f1ldmben fzdgvog f19k5jia fzv833z f1r8x5cv f1687457 fe71wc6 f1wco4xj f103jgab f12fk6ok f4j985t fv86erk f1b4dd42 f1qwtq3x f13gvrpj f132if0v f1vblqk2 fkxsc7s f18j3zqo f1386a7m f1w3nze9 f14pe09c f4skf1h f1lq0dgc f1jgwha3 fbf5vau fc8703c&quot;&gt;
&lt;div class=&quot;___ysqij10 fonrc96 f1d3un5n fdk4ulh f1cijgmw fzb3wgo fssjx5q f11d9hfk fee5icb fr1uqzy&quot;&gt;
&lt;div class=&quot;___sv9ola0 fj2zk3l f1f4vkoj scriptor-instance-702&quot;&gt;
&lt;div class=&quot;scriptor-canvas scriptor-styled-scrollbar focus-container&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This would permanently remove the asset rather than transferring it into a recycle bin.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;&lt;strong&gt;Monitoring and Troubleshooting&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Whenever automated deletion is introduced, observability becomes extremely important.&lt;/p&gt;
&lt;p&gt;Questions that operations teams will eventually ask include:&lt;/p&gt;
&lt;div class=&quot;___77lcry0 f10pi13n&quot;&gt;
&lt;div class=&quot;___1hewfwb f10pi13n f7oukh6 f5p0z4x&quot;&gt;
&lt;div class=&quot;fui-FluentProvider codeBlock-868fui-FluentProviderr6h5 ___1jk74h0 fzk5nv6 f6n0el9 f7x6x6c f1a9mlr7 flj889f f13g0yyb ffzn0d2&quot;&gt;
&lt;div class=&quot;scriptor-component-code-block NhtKn7PZLzk24rduw4FJVQ== scriptor-codeblock-virtualized&quot;&gt;
&lt;div class=&quot;UkrLoQNicnNa5hsNIz3QLw== SpjJXGAclwYtfY+SokpSTg== ZF-Q2MvOY8aWSpGWW7iKRQ== anUhzybDvd7+8CBdKK7ukg==&quot;&gt;
&lt;div class=&quot;AHKpBuU8t+rAbDNImLlOHQ== lHY5JJVFnU8S-jx-OGwJkg== IUtI8YkF5Dl2LTlcjMDTwg==&quot;&gt;
&lt;div class=&quot;___149cboo fssjx5q f11d9hfk fj2zk3l fmyxm6j f1ldmben f1rkeeuc&quot;&gt;
&lt;div class=&quot;___svqwvq0 fssjx5q f11d9hfk fj2zk3l f1ldmben fzdgvog f19k5jia fzv833z f1r8x5cv f1687457 fe71wc6 f1wco4xj f103jgab f12fk6ok f4j985t fv86erk f1b4dd42 f1qwtq3x f13gvrpj f132if0v f1vblqk2 fkxsc7s f18j3zqo f1386a7m f1w3nze9 f14pe09c f4skf1h f1lq0dgc f1jgwha3 fbf5vau fc8703c&quot;&gt;
&lt;div class=&quot;___ysqij10 fonrc96 f1d3un5n fdk4ulh f1cijgmw fzb3wgo fssjx5q f11d9hfk fee5icb fr1uqzy&quot;&gt;
&lt;div class=&quot;___sv9ola0 fj2zk3l f1f4vkoj scriptor-instance-703&quot;&gt;
&lt;div class=&quot;scriptor-canvas scriptor-styled-scrollbar focus-container&quot;&gt;
&lt;div class=&quot;scriptor-pageContainer&quot;&gt;
&lt;div class=&quot;scriptor-pageFrame scriptor-firstPage scriptor-webView scriptor-content-visibility-auto&quot;&gt;
&lt;div class=&quot;scriptor-code-editor scriptor-code-wrap-on scriptor-pageBody scriptor-webViewPage&quot;&gt;
&lt;ol&gt;
&lt;li class=&quot;scriptor-paragraph&quot;&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Was a delete triggered?&lt;/span&gt;&lt;/li&gt;
&lt;li class=&quot;scriptor-paragraph&quot;&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Which rule triggered it?&lt;/span&gt;&lt;/li&gt;
&lt;li class=&quot;scriptor-paragraph&quot;&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Which asset was deleted?&lt;/span&gt;&lt;/li&gt;
&lt;li class=&quot;scriptor-paragraph&quot;&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;When did it occur?&lt;/span&gt;&lt;/li&gt;
&lt;li class=&quot;scriptor-paragraph&quot;&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;What response was returned?&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;scriptor-paragraph&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;scriptor-paragraph&quot;&gt;A potential audit trail might capture:&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Asset identifier&lt;/li&gt;
&lt;li&gt;Event timestamp&lt;/li&gt;
&lt;li&gt;Validation results&lt;/li&gt;
&lt;li&gt;Environment&lt;/li&gt;
&lt;li&gt;API response&lt;/li&gt;
&lt;li&gt;Deletion outcome&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;Monitoring Deletion Activity&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;One area that would require validation during implementation is how deletion activity can be monitored within the platform.&lt;/p&gt;
&lt;p&gt;A potential starting point would be reviewing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Search &amp;amp; Navigation portal logs&lt;/li&gt;
&lt;li&gt;Graph activity logs&lt;/li&gt;
&lt;li&gt;API diagnostics&lt;/li&gt;
&lt;li&gt;Application telemetry&lt;/li&gt;
&lt;li&gt;Hosting platform logs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This would help determine whether delete operations are executing successfully and provide additional visibility during troubleshooting.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;&lt;strong&gt;Extending the Existing Integration vs Building a Custom Connector&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;A natural question that often emerges is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If lifecycle governance is required, should we simply build a custom connector?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The answer depends on organizational priorities.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 79.5271%; border-width: 1px; height: 368.425px; border-spacing: 5px; border-style: solid;&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 57.625px;&quot;&gt;
&lt;td style=&quot;text-align: left; vertical-align: top; border-width: 1px; width: 50.4919%; padding: 1px; border-style: solid; height: 57.625px;&quot;&gt;
&lt;h4&gt;&lt;strong&gt;Option 1: Extend the Existing Integration&lt;/strong&gt;&lt;/h4&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; vertical-align: top; border-width: 1px; width: 49.4881%; padding: 1px; border-style: solid; height: 57.625px;&quot;&gt;
&lt;h4&gt;&lt;strong&gt;Option 2: Build a Fully Custom Connector&lt;/strong&gt;&lt;/h4&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 310.8px;&quot;&gt;
&lt;td style=&quot;text-align: left; vertical-align: top; border-width: 1px; width: 50.4919%; padding: 1px; border-style: solid; height: 310.8px;&quot;&gt;
&lt;p&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Bynder&amp;nbsp; &amp;gt;&amp;nbsp; &lt;/span&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;OOTB Connector&amp;nbsp; &amp;gt;&amp;nbsp; &lt;/span&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Optimizely &lt;/span&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;+ Governance Layer&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Faster implementation&lt;/li&gt;
&lt;li&gt;Lower maintenance overhead&lt;/li&gt;
&lt;li&gt;Vendor-supported synchronization&lt;/li&gt;
&lt;li&gt;Smaller operational footprint&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Potential limitations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Less control over synchronization internals&lt;/li&gt;
&lt;li&gt;Dependency on platform capabilities&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; vertical-align: top; border-width: 1px; width: 49.4881%; padding: 1px; border-style: solid; height: 310.8px;&quot;&gt;
&lt;div class=&quot;___77lcry0 f10pi13n&quot;&gt;
&lt;div class=&quot;___1hewfwb f10pi13n f7oukh6 f5p0z4x&quot;&gt;
&lt;div class=&quot;fui-FluentProvider codeBlock-9357fui-FluentProviderr6hn ___1jk74h0 fzk5nv6 f6n0el9 f7x6x6c f1a9mlr7 flj889f f13g0yyb ffzn0d2&quot;&gt;
&lt;div class=&quot;scriptor-component-code-block NhtKn7PZLzk24rduw4FJVQ== scriptor-codeblock-virtualized&quot;&gt;
&lt;div class=&quot;UkrLoQNicnNa5hsNIz3QLw== SpjJXGAclwYtfY+SokpSTg== ZF-Q2MvOY8aWSpGWW7iKRQ== anUhzybDvd7+8CBdKK7ukg==&quot;&gt;
&lt;div class=&quot;AHKpBuU8t+rAbDNImLlOHQ== lHY5JJVFnU8S-jx-OGwJkg== IUtI8YkF5Dl2LTlcjMDTwg==&quot;&gt;
&lt;div class=&quot;___149cboo fssjx5q f11d9hfk fj2zk3l fmyxm6j f1ldmben f1rkeeuc&quot;&gt;
&lt;div class=&quot;___svqwvq0 fssjx5q f11d9hfk fj2zk3l f1ldmben fzdgvog f19k5jia fzv833z f1r8x5cv f1687457 fe71wc6 f1wco4xj f103jgab f12fk6ok f4j985t fv86erk f1b4dd42 f1qwtq3x f13gvrpj f132if0v f1vblqk2 fkxsc7s f18j3zqo f1386a7m f1w3nze9 f14pe09c f4skf1h f1lq0dgc f1jgwha3 fbf5vau fc8703c&quot;&gt;
&lt;div class=&quot;___ysqij10 fonrc96 f1d3un5n fdk4ulh f1cijgmw fzb3wgo fssjx5q f11d9hfk fee5icb fr1uqzy&quot;&gt;
&lt;div class=&quot;___sv9ola0 fj2zk3l f1f4vkoj scriptor-instance-705&quot;&gt;
&lt;div class=&quot;scriptor-canvas scriptor-styled-scrollbar focus-container&quot;&gt;
&lt;div class=&quot;scriptor-pageContainer&quot;&gt;
&lt;div class=&quot;scriptor-pageFrame scriptor-firstPage scriptor-webView scriptor-content-visibility-auto&quot;&gt;
&lt;div class=&quot;scriptor-code-editor scriptor-code-wrap-on scriptor-pageBody scriptor-webViewPage&quot;&gt;
&lt;div class=&quot;scriptor-paragraph&quot;&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Bynder&amp;nbsp; &amp;gt;&amp;nbsp; &lt;/span&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Custom Connector&amp;nbsp; &amp;gt;&amp;nbsp; &lt;/span&gt;&lt;span class=&quot;scriptor-textRun scriptor-inline&quot;&gt;Optimizely&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;scriptor-paragraph&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;scriptor-paragraph&quot;&gt;Advantages:&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Complete control&lt;/li&gt;
&lt;li&gt;Custom business logic&lt;/li&gt;
&lt;li&gt;Full synchronization ownership&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Potential considerations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Authentication management&lt;/li&gt;
&lt;li&gt;Delta synchronization&lt;/li&gt;
&lt;li&gt;Retry handling&lt;/li&gt;
&lt;li&gt;Error recovery&lt;/li&gt;
&lt;li&gt;Schema evolution&lt;/li&gt;
&lt;li&gt;Long-term maintenance costs&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&quot;___77lcry0 f10pi13n&quot;&gt;
&lt;div class=&quot;___1hewfwb f10pi13n f7oukh6 f5p0z4x&quot;&gt;
&lt;div class=&quot;fui-FluentProvider codeBlock-3356fui-FluentProviderr6he ___1jk74h0 fzk5nv6 f6n0el9 f7x6x6c f1a9mlr7 flj889f f13g0yyb ffzn0d2&quot;&gt;
&lt;div class=&quot;scriptor-component-code-block NhtKn7PZLzk24rduw4FJVQ== scriptor-codeblock-virtualized&quot;&gt;
&lt;div class=&quot;UkrLoQNicnNa5hsNIz3QLw== SpjJXGAclwYtfY+SokpSTg== ZF-Q2MvOY8aWSpGWW7iKRQ== anUhzybDvd7+8CBdKK7ukg==&quot;&gt;
&lt;div class=&quot;AHKpBuU8t+rAbDNImLlOHQ== lHY5JJVFnU8S-jx-OGwJkg== IUtI8YkF5Dl2LTlcjMDTwg==&quot;&gt;
&lt;div class=&quot;___149cboo fssjx5q f11d9hfk fj2zk3l fmyxm6j f1ldmben f1rkeeuc&quot;&gt;
&lt;div class=&quot;___svqwvq0 fssjx5q f11d9hfk fj2zk3l f1ldmben fzdgvog f19k5jia fzv833z f1r8x5cv f1687457 fe71wc6 f1wco4xj f103jgab f12fk6ok f4j985t fv86erk f1b4dd42 f1qwtq3x f13gvrpj f132if0v f1vblqk2 fkxsc7s f18j3zqo f1386a7m f1w3nze9 f14pe09c f4skf1h f1lq0dgc f1jgwha3 fbf5vau fc8703c&quot;&gt;
&lt;div class=&quot;___ysqij10 fonrc96 f1d3un5n fdk4ulh f1cijgmw fzb3wgo fssjx5q f11d9hfk fee5icb fr1uqzy&quot;&gt;
&lt;div class=&quot;___sv9ola0 fj2zk3l f1f4vkoj scriptor-instance-704&quot;&gt;
&lt;div class=&quot;scriptor-canvas scriptor-styled-scrollbar focus-container&quot;&gt;
&lt;div class=&quot;scriptor-pageContainer&quot;&gt;
&lt;div class=&quot;scriptor-pageFrame scriptor-firstPage scriptor-webView scriptor-content-visibility-auto&quot;&gt;
&lt;div class=&quot;scriptor-code-editor scriptor-code-wrap-on scriptor-pageBody scriptor-webViewPage&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For many organizations, extending an existing integration with additional governance capabilities may represent a more balanced approach than replacing the synchronization engine altogether.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;&lt;strong&gt;Assumptions and Validation Status&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;The approaches discussed throughout this article are based on architectural evaluation and platform capability analysis rather than a fully validated production implementation.&lt;/p&gt;
&lt;p&gt;At the time of writing, the proposed event-driven webhook model remains an architectural concept that appears technically achievable based on the currently available capabilities of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Bynder webhooks&lt;/li&gt;
&lt;li&gt;Bynder permanent tokens&lt;/li&gt;
&lt;li&gt;Optimizely Content Management /Graph APIs&lt;/li&gt;
&lt;li&gt;Optimizely Graph reference lookups&lt;/li&gt;
&lt;li&gt;Standard event-driven integration patterns&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, several areas would benefit from additional validation before adoption within a production environment:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Large-scale webhook processing behavior&lt;/li&gt;
&lt;li&gt;API rate limiting and throttling characteristics&lt;/li&gt;
&lt;li&gt;Long-running operational reliability&lt;/li&gt;
&lt;li&gt;Deletion timing and synchronization race conditions&lt;/li&gt;
&lt;li&gt;Monitoring and alerting strategies&lt;/li&gt;
&lt;li&gt;Reconciliation requirements for lost events&lt;/li&gt;
&lt;li&gt;Long-term maintenance implications&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As with any architectural pattern, organizations should conduct their own validation, performance testing, and operational reviews before implementing the approach within production workloads.&lt;/p&gt;
&lt;div&gt;&lt;hr /&gt;
&lt;h2&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Asset synchronization is only one stage in the lifecycle of an enterprise DAM integration.&lt;/p&gt;
&lt;p&gt;As asset repositories grow, organizations may also need to consider how visibility changes, environment allocation, content relationships, and cleanup processes should be handled over time.&lt;/p&gt;
&lt;p&gt;The approaches discussed in this article should be viewed as architectural considerations rather than implementation recommendations. While the proposed event-driven governance model appears technically feasible based on the capabilities provided by Bynder, Optimizely SaaS CMS, and Optimizely Graph, additional validation and testing would be required before considering it for production adoption.&lt;/p&gt;
&lt;p&gt;Ultimately, long-term success is unlikely to depend solely on controlling what enters the system. It may also require thoughtful approaches to governing assets throughout their lifecycle&amp;mdash;from synchronization and distribution through validation, retention, and eventual removal.&lt;/p&gt;
&lt;hr /&gt;
&lt;h4 class=&quot;_1l4j4na1 _1qskm0h c8urk _1a0p899 _5hqrjx _667h42&quot;&gt;&#128218; Bynder + Optimizely SaaS CMS Integration Series&lt;/h4&gt;
&lt;div class=&quot;_1l4j4na3 _667h42&quot;&gt;&lt;em class=&quot;far4bl _667h42&quot;&gt;This article is the final installment of a 3-part comprehensive architecture guide:&lt;/em&gt;&lt;/div&gt;
&lt;ul class=&quot;_667h42 _1l4j4na6&quot;&gt;
&lt;li class=&quot;_667h42 _1l4j4na2&quot;&gt;&lt;strong class=&quot;far4bl _1a0p899 _667h42&quot;&gt;✅ Part 1:&amp;nbsp;&lt;a class=&quot;_667h42 _1l4j4nac _1c9dtv3 _1by2b93 _1140xq1 _667h42&quot; href=&quot;/link/4f215cd334254dfcbc121d5f4e20edd6.aspx&quot;&gt;Implementing the Connector &amp;mdash; Lessons Learned&lt;/a&gt;&lt;/strong&gt;&lt;br /&gt;&lt;em class=&quot;far4bl _667h42&quot;&gt;What metadata syncs, how URLs behave, and how to query/render assets in a Next.js frontend.&lt;br /&gt;&lt;br /&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li class=&quot;_667h42 _1l4j4na2&quot;&gt;&lt;strong class=&quot;far4bl _1a0p899 _667h42&quot;&gt;✅ Part 2:&amp;nbsp;&lt;a class=&quot;_667h42 _1l4j4nac _1c9dtv3 _1by2b93 _1140xq1 _667h42&quot; href=&quot;/link/30275ac47c4843148eeda8b7d6ed282a.aspx&quot;&gt;Planning &amp;amp; Filtering &amp;mdash; Avoiding Asset Sprawl and Unnecessary Sync&lt;/a&gt;&lt;/strong&gt;&lt;br /&gt;&lt;em class=&quot;far4bl _667h42&quot;&gt;How we achieved metadata-based sync filtering with zero custom code.&lt;br /&gt;&lt;br /&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li class=&quot;_667h42 _1l4j4na2&quot;&gt;&lt;strong class=&quot;far4bl _1a0p899 _667h42&quot;&gt;&#128073; Part 3: Exploring Asset Lifecycle Management &amp;amp; Three-Gate Governance&lt;/strong&gt;&amp;nbsp;&lt;em class=&quot;far4bl _667h42&quot;&gt;(You are here)&lt;/em&gt;&lt;br /&gt;&lt;em class=&quot;far4bl _667h42&quot;&gt;A webhook-driven, three-gate model to handle asset updates, environments, and usage verification.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;</id><updated>2026-07-05T07:44:36.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Planning Your Bynder DAM and Optimizely SaaS CMS Integration the Right Way: Avoiding Asset Sprawl and Unnecessary Synchronization</title><link href="https://world.optimizely.com/blogs/vipin-banka--learnings--insights/dates/2026/7/planning-your-bynder-dam-integration-for-optimizely-saas-cms-avoiding-asset-sprawl-managing-costs-and-designing-for-scale/" /><id>&lt;p class=&quot;MsoNormal&quot;&gt;&lt;span class=&quot;far4bl _667h42&quot;&gt;Note: This is Part 2 of our Bynder integration series. If you missed the Part 1, check out &quot;&lt;a href=&quot;/link/4f215cd334254dfcbc121d5f4e20edd6.aspx&quot;&gt;Implementing the Bynder DAM Connector with Optimizely SaaS CMS: Lessons Learned&lt;/a&gt;&quot;.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;In my previous article, I explored how the Bynder DAM Connector integrates with Optimizely SaaS CMS and how synchronized assets become available through Optimizely Graph and the CMS authoring experience.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;This article focuses specifically on the Out-of-the-Box (OOTB) Bynder Connector available through Optimizely Connect Platform (OCP). The goal is to understand how much control can be achieved using standard Optimizely and Bynder capabilities before introducing custom integrations, middleware, or synchronization processes.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;While the technical setup of the OOTB connector is relatively straightforward, I discovered that the most important decisions actually happen&amp;nbsp;&lt;em&gt;before&lt;/em&gt; the first synchronization is ever executed.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Many implementation teams focus on establishing the connection between Bynder and Optimizely. However, a more important architectural question is:&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;&lt;strong&gt;What assets should Optimizely be allowed to see in the first place?&lt;/strong&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;For organizations that use Bynder as a centralized Digital Asset Management platform, the answer has significant implications for governance, editor experience, operational overhead, and long-term scalability.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;This article focuses on the planning and governance considerations that should be addressed before enabling the Bynder connector.&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: center;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h3 class=&quot;MsoNormal&quot;&gt;&lt;strong&gt;Understanding the Default Connector Behavior&lt;/strong&gt;&lt;/h3&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;The Out-of-the-Box (OOTB) Bynder connector synchronizes assets that are visible to the authenticated Bynder account.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;In simple terms, the connector does not determine which assets should be synchronized. Bynder determines that by evaluating the permissions of the account used during the authorization process.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;As a result, the overall scope of synchronization is directly influenced by:&lt;/p&gt;
&lt;ul style=&quot;margin-top: 0cm;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l10 level1 lfo1; tab-stops: list 36.0pt;&quot;&gt;The permission profile assigned to the integration account&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l10 level1 lfo1; tab-stops: list 36.0pt;&quot;&gt;Asset visibility settings within Bynder&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l10 level1 lfo1; tab-stops: list 36.0pt;&quot;&gt;Metadata visibility rules&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l10 level1 lfo1; tab-stops: list 36.0pt;&quot;&gt;The account used during connector authorization&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;The connector can only synchronize assets that the authenticated account can access.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;This seemingly simple behavior becomes extremely important when working with large DAM implementations.&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: center;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h3 class=&quot;MsoNormal&quot;&gt;&lt;strong&gt;Why Asset Scope Matters&lt;/strong&gt;&lt;/h3&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;In many organizations, Bynder contains assets for multiple business functions and audiences.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Examples include:&lt;/p&gt;
&lt;ul style=&quot;margin-top: 0cm;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l3 level1 lfo2; tab-stops: list 36.0pt;&quot;&gt;Website assets&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l3 level1 lfo2; tab-stops: list 36.0pt;&quot;&gt;Campaign assets&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l3 level1 lfo2; tab-stops: list 36.0pt;&quot;&gt;Product photography&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l3 level1 lfo2; tab-stops: list 36.0pt;&quot;&gt;Internal communications&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l3 level1 lfo2; tab-stops: list 36.0pt;&quot;&gt;Regional marketing materials&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l3 level1 lfo2; tab-stops: list 36.0pt;&quot;&gt;Archived content&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l3 level1 lfo2; tab-stops: list 36.0pt;&quot;&gt;Partner-specific assets&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l3 level1 lfo2; tab-stops: list 36.0pt;&quot;&gt;Brand assets&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Not all of these assets are intended for websites powered by Optimizely.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Without careful planning, the connector can synchronize substantially more assets than editors actually need.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;At first glance this may not appear to be a major concern. However, every synchronized asset becomes another content item managed by the Optimizely ecosystem.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;As synchronization volume grows:&lt;/p&gt;
&lt;ul style=&quot;margin-top: 0cm;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l12 level1 lfo3; tab-stops: list 36.0pt;&quot;&gt;More items are stored in Optimizely Graph&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l12 level1 lfo3; tab-stops: list 36.0pt;&quot;&gt;More content records are created&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l12 level1 lfo3; tab-stops: list 36.0pt;&quot;&gt;More synchronization events are processed&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l12 level1 lfo3; tab-stops: list 36.0pt;&quot;&gt;Asset selection becomes harder for editors&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l12 level1 lfo3; tab-stops: list 36.0pt;&quot;&gt;Governance becomes increasingly complex&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Most importantly, organizations should understand how content volume contributes to overall platform usage and operational overhead.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;For this reason, a successful DAM strategy is not about synchronizing everything that exists inside Bynder.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;It is about synchronizing only what is required.&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: center;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h3 class=&quot;MsoNormal&quot;&gt;&lt;strong&gt;Start With Governance, Not Technology&lt;/strong&gt;&lt;/h3&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;One of the biggest lessons learned during implementation was that DAM integrations are primarily a governance challenge rather than a technical challenge.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;The connector itself can be configured very quickly.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Determining which assets should be synchronized requires considerably more thought.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Before connecting Bynder to Optimizely, I recommend answering questions such as:&lt;/p&gt;
&lt;ul style=&quot;margin-top: 0cm;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l14 level1 lfo4; tab-stops: list 36.0pt;&quot;&gt;Which assets are intended for public websites?&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l14 level1 lfo4; tab-stops: list 36.0pt;&quot;&gt;Which business teams own those assets?&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l14 level1 lfo4; tab-stops: list 36.0pt;&quot;&gt;Which environments require those assets?&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l14 level1 lfo4; tab-stops: list 36.0pt;&quot;&gt;Who approves synchronization scope changes?&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l14 level1 lfo4; tab-stops: list 36.0pt;&quot;&gt;How will future asset growth be managed?&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Answering these questions early can prevent a significant amount of cleanup work later.&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: center;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h3 class=&quot;MsoNormal&quot;&gt;&lt;strong&gt;The Common Mistake: Connecting With an Administrator Account&lt;/strong&gt;&lt;/h3&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;The easiest way to enable the connector is often to authenticate using an administrator account.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;The workflow usually looks something like this:&lt;/p&gt;
&lt;ol style=&quot;margin-top: 0cm;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l4 level1 lfo5; tab-stops: list 36.0pt;&quot;&gt;Open the Bynder connector.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l4 level1 lfo5; tab-stops: list 36.0pt;&quot;&gt;Click Connect.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l4 level1 lfo5; tab-stops: list 36.0pt;&quot;&gt;Log in using administrative credentials.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l4 level1 lfo5; tab-stops: list 36.0pt;&quot;&gt;Approve access.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l4 level1 lfo5; tab-stops: list 36.0pt;&quot;&gt;Start synchronizing.&lt;/li&gt;
&lt;/ol&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;The problem is that administrator accounts often see virtually everything.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;That means the connector can potentially see:&lt;/p&gt;
&lt;ul style=&quot;margin-top: 0cm;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l8 level1 lfo6; tab-stops: list 36.0pt;&quot;&gt;Website assets&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l8 level1 lfo6; tab-stops: list 36.0pt;&quot;&gt;Internal assets&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l8 level1 lfo6; tab-stops: list 36.0pt;&quot;&gt;Historical assets&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l8 level1 lfo6; tab-stops: list 36.0pt;&quot;&gt;Regional assets&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l8 level1 lfo6; tab-stops: list 36.0pt;&quot;&gt;Unused assets&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l8 level1 lfo6; tab-stops: list 36.0pt;&quot;&gt;Archived assets&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;In other words, the connector effectively inherits access to the entire DAM repository.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;This removes any meaningful synchronization boundaries.&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: center;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h3 class=&quot;MsoNormal&quot;&gt;&lt;strong&gt;Why Creating a New User Is Not Enough&lt;/strong&gt;&lt;/h3&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;A common assumption is:&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;&quot;I&#39;ll simply create a new user for Optimizely and restrict its permissions.&quot;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Unfortunately, this is where many implementations run into trouble.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Bynder permissions are primarily controlled through Permission Profiles.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;The effective model looks like this:&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;&lt;strong&gt;Permission Profile&amp;nbsp; &amp;rarr;&amp;nbsp; User&amp;nbsp; &amp;rarr;&amp;nbsp; Asset Visibility&lt;/strong&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;This means that simply creating a new user does not automatically reduce visibility.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;If the new integration user remains associated with a highly privileged profile, that user may still inherit broad access to assets.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;In practice, creating a new user and placing it within an administrator-level permission profile often results in another highly privileged user.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;The desired filtering behavior never materializes.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;This is why a dedicated Permission Profile becomes essential.&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: center;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h3 class=&quot;MsoNormal&quot;&gt;&lt;strong&gt;The Recommended Approach&lt;/strong&gt;&lt;/h3&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Instead of creating a user within an existing administrative profile, create:&lt;/p&gt;
&lt;ul style=&quot;margin-top: 0cm;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l1 level1 lfo7; tab-stops: list 36.0pt;&quot;&gt;A dedicated synchronization user&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l1 level1 lfo7; tab-stops: list 36.0pt;&quot;&gt;A dedicated synchronization permission profile&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l1 level1 lfo7; tab-stops: list 36.0pt;&quot;&gt;Dedicated metadata visibility rules&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Together, these become the foundation of your synchronization strategy.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;The goal is to create an account that acts as a read-only integration consumer and nothing more.&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: center;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h4&gt;&lt;strong&gt;1. Creating a Dedicated Synchronization Profile&lt;/strong&gt;&lt;/h4&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;The dedicated synchronization profile should be configured with only the permissions required for integration purposes.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Recommended permissions include:&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;&lt;strong&gt;Asset Bank&lt;/strong&gt;&lt;/p&gt;
&lt;ul style=&quot;margin-top: 0cm;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l15 level1 lfo8; tab-stops: list 36.0pt;&quot;&gt;View assets&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l15 level1 lfo8; tab-stops: list 36.0pt;&quot;&gt;Search assets&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l15 level1 lfo8; tab-stops: list 36.0pt;&quot;&gt;View asset details&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l15 level1 lfo8; tab-stops: list 36.0pt;&quot;&gt;View metaproperties&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;&lt;strong&gt;General / Integration&lt;/strong&gt;&lt;/p&gt;
&lt;ul style=&quot;margin-top: 0cm;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l2 level1 lfo9; tab-stops: list 36.0pt;&quot;&gt;API Access&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l2 level1 lfo9; tab-stops: list 36.0pt;&quot;&gt;Manage Webhooks&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Permissions that generally do not need to be enabled include:&lt;/p&gt;
&lt;ul style=&quot;margin-top: 0cm;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l7 level1 lfo10; tab-stops: list 36.0pt;&quot;&gt;Upload assets&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l7 level1 lfo10; tab-stops: list 36.0pt;&quot;&gt;Edit assets&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l7 level1 lfo10; tab-stops: list 36.0pt;&quot;&gt;Delete assets&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l7 level1 lfo10; tab-stops: list 36.0pt;&quot;&gt;Manage taxonomy&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l7 level1 lfo10; tab-stops: list 36.0pt;&quot;&gt;Administrative functions&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: center;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;h4&gt;&lt;strong&gt;2. Create a Dedicated Synchronization Account&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Once the synchronization profile has been configured, create a dedicated service account and assign it exclusively to that profile.&lt;/p&gt;
&lt;p&gt;This account will be used during the Bynder authorization process and will ultimately determine which assets the Optimizely connector can access.&lt;/p&gt;
&lt;p&gt;Avoid assigning the account to an existing administrative profile or a profile used by content creators. The goal is to create a narrowly scoped integration account whose sole responsibility is synchronizing approved assets into Optimizely.&lt;/p&gt;
&lt;p&gt;By separating the integration account from day-to-day user accounts, you gain:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Predictable synchronization behavior&lt;/li&gt;
&lt;li&gt;Easier troubleshooting&lt;/li&gt;
&lt;li&gt;Clear ownership of integration permissions&lt;/li&gt;
&lt;li&gt;Reduced risk of unintentionally exposing additional assets to Optimizely&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: center;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h4 class=&quot;MsoNormal&quot;&gt;&lt;strong&gt;3. Using Metadata as a Synchronization Boundary&lt;/strong&gt;&lt;/h4&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Many teams think about metadata exclusively in terms of search and categorization.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;During this implementation, I found it equally useful to think of metadata as a governance mechanism.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Metadata can help answer questions such as:&lt;/p&gt;
&lt;ul style=&quot;margin-top: 0cm;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l13 level1 lfo11; tab-stops: list 36.0pt;&quot;&gt;Should this asset be visible to websites?&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l13 level1 lfo11; tab-stops: list 36.0pt;&quot;&gt;Which business unit owns this asset?&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l13 level1 lfo11; tab-stops: list 36.0pt;&quot;&gt;Which region can use this asset?&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l13 level1 lfo11; tab-stops: list 36.0pt;&quot;&gt;Which environments should receive this asset?&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;By implementing metadata-driven visibility, organizations can define synchronization boundaries before assets enter Optimizely.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;This keeps governance as close as possible to the source system.&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: center;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h4 class=&quot;MsoNormal&quot;&gt;&lt;strong&gt;4. Configuring Metadata Visibility&lt;/strong&gt;&lt;/h4&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;One effective approach is to use a &lt;strong&gt;required metadata property&lt;/strong&gt; that identifies whether an asset is intended for website consumption.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;For example: &lt;strong&gt;VisibleTo&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;img style=&quot;border-width: 1px; border-style: solid;&quot; src=&quot;/link/9b93cf8ded06433fb473d4ee88ef7f0d.aspx?1783186950489&quot; width=&quot;447&quot; height=&quot;174&quot; /&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;To configure this:&lt;/p&gt;
&lt;ol style=&quot;margin-top: 0cm;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l17 level1 lfo12; tab-stops: list 36.0pt;&quot;&gt;Navigate to &lt;strong&gt;Settings&amp;nbsp; &amp;rarr;&amp;nbsp; Taxonomy&amp;nbsp; &amp;rarr;&amp;nbsp; Metaproperties Management&lt;/strong&gt;&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l17 level1 lfo12; tab-stops: list 36.0pt;&quot;&gt;Select the metadata property&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l17 level1 lfo12; tab-stops: list 36.0pt;&quot;&gt;Open the &lt;strong&gt;Visibility&lt;/strong&gt; configuration&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l17 level1 lfo12; tab-stops: list 36.0pt;&quot;&gt;Select the synchronization profile&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l17 level1 lfo12; tab-stops: list 36.0pt;&quot;&gt;Grant/deny visibility only to approved metadata values.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img style=&quot;border-width: 1px; border-style: solid;&quot; src=&quot;/link/f4763bfeca7d40b780f19110f800e0c8.aspx?1783187024542&quot; width=&quot;564&quot; height=&quot;330&quot; /&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;When configured correctly, the synchronization account becomes effectively blind to assets outside the approved scope.&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: center;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h4 class=&quot;MsoNormal&quot;&gt;&lt;strong&gt;5. Validate Before Connecting&lt;/strong&gt;&lt;/h4&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;One of the most valuable steps in the entire process happens before Optimizely is connected.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;After creating the synchronization account and permission profile:&lt;/p&gt;
&lt;ol style=&quot;margin-top: 0cm;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l11 level1 lfo13; tab-stops: list 36.0pt;&quot;&gt;Open an Incognito or InPrivate browser session.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l11 level1 lfo13; tab-stops: list 36.0pt;&quot;&gt;Login using the synchronization account.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l11 level1 lfo13; tab-stops: list 36.0pt;&quot;&gt;Search for assets.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l11 level1 lfo13; tab-stops: list 36.0pt;&quot;&gt;Browse collections.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l11 level1 lfo13; tab-stops: list 36.0pt;&quot;&gt;Verify metadata visibility.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l11 level1 lfo13; tab-stops: list 36.0pt;&quot;&gt;Validate that only the intended assets are available.&lt;/li&gt;
&lt;/ol&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Ask yourself:&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;If I connect Optimizely using this account right now, am I comfortable synchronizing every asset visible on this screen?&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;If the answer is no, continue refining the profile before proceeding.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;This simple validation step can save considerable troubleshooting effort later.&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: center;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h3 class=&quot;MsoNormal&quot;&gt;&lt;strong&gt;The Authorization Flow Trap&lt;/strong&gt;&lt;/h3&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Another lesson learned during implementation is that the authorization process deserves more attention than most teams initially give it.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;The connector authorization flow uses the account that participates in the OAuth approval process.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;If an existing browser session already contains administrative Bynder credentials, those credentials may inadvertently be used during authorization.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;To avoid this:&lt;/p&gt;
&lt;ol style=&quot;margin-top: 0cm;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l6 level1 lfo14; tab-stops: list 36.0pt;&quot;&gt;Open a fresh InPrivate or Incognito browser window.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l6 level1 lfo14; tab-stops: list 36.0pt;&quot;&gt;Login to Optimizely SaaS CMS.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l6 level1 lfo14; tab-stops: list 36.0pt;&quot;&gt;Start the Bynder App installation and connection process.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;font-weight: bold;&quot;&gt;&lt;strong&gt;Most Important:&lt;/strong&gt;
&lt;ol style=&quot;margin-top: 0cm;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l6 level1 lfo14; tab-stops: list 36.0pt;&quot;&gt;Installation process must open the Bynder login page.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l6 level1 lfo14; tab-stops: list 36.0pt;&quot;&gt;Login using the dedicated synchronization account.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l6 level1 lfo14; tab-stops: list 36.0pt;&quot;&gt;Complete the authorization flow.&lt;/li&gt;
&lt;/ol&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Using a clean browser session helps ensure the connector is authorized with the intended account rather than accidentally inheriting access from an existing administrative session.&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: center;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h4 class=&quot;MsoNormal&quot;&gt;&lt;strong&gt;Understanding the Synchronization Flow&lt;/strong&gt;&lt;/h4&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;&lt;strong&gt;Scenario A : Asset is visible to the synchronization account&lt;/strong&gt;&lt;/p&gt;
&lt;ul style=&quot;margin-top: 0cm;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo15; tab-stops: list 36.0pt;&quot;&gt;Asset uploaded/updated into Bynder.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo15; tab-stops: list 36.0pt;&quot;&gt;Bynder triggers the OCP connector webhook.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo15; tab-stops: list 36.0pt;&quot;&gt;Bynder receives a request from the connector.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo15; tab-stops: list 36.0pt;&quot;&gt;The synchronization account is authorized to view the asset.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo15; tab-stops: list 36.0pt;&quot;&gt;Bynder returns the asset payload.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo15; tab-stops: list 36.0pt;&quot;&gt;OCP processes the response.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo15; tab-stops: list 36.0pt;&quot;&gt;The asset is synchronized into Optimizely Graph.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo15; tab-stops: list 36.0pt;&quot;&gt;The asset may become available within CMS depending on configuration.&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;&lt;img style=&quot;border-width: 1px; border-style: solid;&quot; src=&quot;/link/a927f1a971c74079be8d3e97e45419be.aspx?1783187744711&quot; width=&quot;582&quot; height=&quot;391&quot; /&gt;&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: center;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;&lt;strong&gt;Scenario B : Asset is not visible to the synchronization account&lt;/strong&gt;&lt;/p&gt;
&lt;ul style=&quot;margin-top: 0cm;&quot;&gt;
&lt;li&gt;Asset uploaded/updated into Bynder.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo15; tab-stops: list 36.0pt;&quot;&gt;Bynder triggers the OCP connector webhook.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo15; tab-stops: list 36.0pt;&quot;&gt;Bynder receives a request from the connector.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l9 level1 lfo16; tab-stops: list 36.0pt;&quot;&gt;Bynder evaluates permissions.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l9 level1 lfo16; tab-stops: list 36.0pt;&quot;&gt;Access is denied because the synchronization account cannot see the asset.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l9 level1 lfo16; tab-stops: list 36.0pt;&quot;&gt;No asset payload is returned.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l9 level1 lfo16; tab-stops: list 36.0pt;&quot;&gt;OCP connector skips synchronization.&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;This means filtering occurs naturally through the permission model rather than through custom synchronization logic.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;&lt;img style=&quot;border-width: 1px; border-style: solid;&quot; src=&quot;/link/cae36a1cec544c05918e7bcee09a60d8.aspx?1783187879183&quot; width=&quot;447&quot; height=&quot;428&quot; /&gt;&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: center;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h3 class=&quot;MsoNormal&quot;&gt;&lt;strong&gt;Key Lessons Learned&lt;/strong&gt;&lt;/h3&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;One of the biggest realizations are that successful DAM integrations are defined long before the first asset is synchronized.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;A few recommendations I would strongly encourage:&lt;/p&gt;
&lt;ul style=&quot;margin-top: 0cm;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l5 level1 lfo18; tab-stops: list 36.0pt;&quot;&gt;Define synchronization boundaries before configuring the connector.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l5 level1 lfo18; tab-stops: list 36.0pt;&quot;&gt;Create a dedicated integration profile instead of reusing administrative profiles.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l5 level1 lfo18; tab-stops: list 36.0pt;&quot;&gt;Use metadata strategically rather than treating it purely as a categorization tool.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l5 level1 lfo18; tab-stops: list 36.0pt;&quot;&gt;Validate permissions using a private browser session before connecting Optimizely.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l5 level1 lfo18; tab-stops: list 36.0pt;&quot;&gt;Think carefully about which assets truly belong inside the website ecosystem.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l5 level1 lfo18; tab-stops: list 36.0pt;&quot;&gt;Start with a smaller, controlled asset scope and expand incrementally if needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Most importantly:&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;&lt;strong&gt;Just because an asset exists in Bynder does not mean it needs to exist in Optimizely.&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: center;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h3 class=&quot;MsoNormal&quot;&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/h3&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;The Bynder connector makes it very easy to bring DAM assets into Optimizely SaaS CMS.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;However, the long-term success of the integration depends less on connector configuration and more on governance decisions made beforehand.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;By investing time in permission profiles, metadata design, synchronization boundaries, and dedicated integration accounts, organizations can significantly reduce operational overhead and create a much cleaner experience for both editors and developers.&lt;/p&gt;
&lt;div&gt;In my next article, I&#39;ll take a deeper look at what happens after synchronization is established. We&#39;ll explore how asset visibility changes can create orphan records, how synchronization behavior is impacted when assets move in and out of scope, and the operational challenges that emerge in multi-environment implementations. I&#39;ll also discuss environment-specific asset governance considerations, common pitfalls organizations encounter as their DAM footprint grows, and architectural approaches for keeping Optimizely environments clean, scalable, and maintainable over time.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: center;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;h4 class=&quot;_1l4j4na1 _1qskm0h c8urk _1a0p899 _5hqrjx _667h42&quot;&gt;&#128218; Bynder + Optimizely SaaS CMS Integration Series&lt;/h4&gt;
&lt;div class=&quot;_1l4j4na3 _667h42&quot;&gt;&lt;em class=&quot;far4bl _667h42&quot;&gt;This article is the second installment of a 3-part comprehensive architecture guide:&lt;/em&gt;&lt;/div&gt;
&lt;ul class=&quot;_667h42 _1l4j4na6&quot;&gt;
&lt;li class=&quot;_667h42 _1l4j4na2&quot;&gt;&lt;strong class=&quot;far4bl _1a0p899 _667h42&quot;&gt;✅ Part 1:&amp;nbsp;&lt;a class=&quot;_667h42 _1l4j4nac _1c9dtv3 _1by2b93 _1140xq1 _667h42&quot; href=&quot;/link/4f215cd334254dfcbc121d5f4e20edd6.aspx&quot;&gt;Implementing the Connector &amp;mdash; Lessons Learned&lt;/a&gt;&lt;/strong&gt;&lt;br /&gt;&lt;em class=&quot;far4bl _667h42&quot;&gt;What metadata syncs, how URLs behave, and how to query/render assets in a Next.js frontend.&lt;br /&gt;&lt;br /&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li class=&quot;_667h42 _1l4j4na2&quot;&gt;&lt;strong class=&quot;far4bl _1a0p899 _667h42&quot;&gt;&#128073; Part 2: Planning &amp;amp; Filtering &amp;mdash; Avoiding Asset Sprawl and Unnecessary Sync&lt;/strong&gt;&amp;nbsp;&lt;em class=&quot;far4bl _667h42&quot;&gt;(You are here)&lt;/em&gt;&lt;br /&gt;&lt;em class=&quot;far4bl _667h42&quot;&gt;How we achieved metadata-based sync filtering with zero custom code.&lt;br /&gt;&lt;br /&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li class=&quot;_667h42 _1l4j4na2&quot;&gt;&lt;strong class=&quot;far4bl _1a0p899 _667h42&quot;&gt;⏭️ Part 3:&amp;nbsp;&lt;a class=&quot;_667h42 _1l4j4nac _1c9dtv3 _1by2b93 _1140xq1 _667h42&quot; href=&quot;/link/650c1d72d9a84c28b2355e8942fc7cda.aspx&quot;&gt;Exploring Asset Lifecycle Management &amp;amp; Three-Gate Governance&lt;/a&gt;&lt;/strong&gt;&lt;br /&gt;&lt;em class=&quot;far4bl _667h42&quot;&gt;Explore a webhook-driven, three-gate model to handle asset updates, environments, and usage verification.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;</id><updated>2026-07-04T18:28:12.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Implementing the Bynder DAM Connector with Optimizely SaaS CMS: Lessons Learned</title><link href="https://world.optimizely.com/blogs/vipin-banka--learnings--insights/dates/2026/7/implementing-the-bynder-dam-connector-with-optimizely-saas-cms-lessons-learned/" /><id>&lt;p class=&quot;MsoNormal&quot;&gt;&lt;em&gt;What I learned while integrating Bynder DAM with Optimizely SaaS CMS, exploring Optimizely Graph, and building a headless frontend experience.&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;
&lt;h2 class=&quot;MsoNormal&quot;&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h2&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;While working on an Optimizely SaaS CMS implementation, I spent considerable time understanding how the Bynder DAM connector works, what data gets synchronized, how assets appear in Optimizely Graph, and how frontend applications should consume those assets.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Although both Optimizely and Bynder provide documentation, I found very little content that shows the complete end-to-end picture:&lt;/p&gt;
&lt;ul style=&quot;margin-top: 0cm;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo1; tab-stops: list 36.0pt;&quot;&gt;What metadata gets synchronized?&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo1; tab-stops: list 36.0pt;&quot;&gt;How do assets appear in Optimizely CMS?&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo1; tab-stops: list 36.0pt;&quot;&gt;What does the data look like in Optimizely Graph?&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo1; tab-stops: list 36.0pt;&quot;&gt;How do developers render images, videos, and documents?&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo1; tab-stops: list 36.0pt;&quot;&gt;How do public and private assets behave?&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo1; tab-stops: list 36.0pt;&quot;&gt;What are the limitations of the connector?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span style=&quot;font-size: 12.0pt; line-height: 115%; font-family: &#39;Aptos&#39;,sans-serif; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: Aptos; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: &#39;Times New Roman&#39;; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-IN; mso-fareast-language: EN-US; mso-bidi-language: AR-SA;&quot;&gt;This article summarizes my findings and lessons learned from exploring the integration.&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: center;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h3 class=&quot;MsoNormal&quot;&gt;&lt;strong&gt;What is Optimizely SaaS CMS?&lt;/strong&gt;&lt;/h3&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Optimizely SaaS CMS is a cloud-native content management platform that enables organizations to create and manage content experiences across websites, mobile applications, portals, and other digital channels.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Key capabilities include:&lt;/p&gt;
&lt;ul style=&quot;margin-top: 0cm;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l1 level1 lfo1; tab-stops: list 36.0pt;&quot;&gt;Content modeling&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l1 level1 lfo1; tab-stops: list 36.0pt;&quot;&gt;Headless content delivery&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l1 level1 lfo1; tab-stops: list 36.0pt;&quot;&gt;Visual editing&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l1 level1 lfo1; tab-stops: list 36.0pt;&quot;&gt;Multi-site architecture&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l1 level1 lfo1; tab-stops: list 36.0pt;&quot;&gt;Optimizely Graph integration&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l1 level1 lfo1; tab-stops: list 36.0pt;&quot;&gt;API-first content management&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Unlike traditional CMS platforms, content in Optimizely SaaS is managed through content types and delivered through APIs and Optimizely Graph.&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: center;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h3 class=&quot;MsoNormal&quot;&gt;&lt;strong&gt;What is Bynder?&lt;/strong&gt;&lt;/h3&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Bynder is an enterprise Digital Asset Management (DAM) platform.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Organizations typically use Bynder to manage:&lt;/p&gt;
&lt;ul style=&quot;margin-top: 0cm;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo2; tab-stops: list 36.0pt;&quot;&gt;Images&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo2; tab-stops: list 36.0pt;&quot;&gt;Videos&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo2; tab-stops: list 36.0pt;&quot;&gt;Brand assets&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo2; tab-stops: list 36.0pt;&quot;&gt;Product photography&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo2; tab-stops: list 36.0pt;&quot;&gt;Brochures and documents&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo2; tab-stops: list 36.0pt;&quot;&gt;Marketing collateral&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo2; tab-stops: list 36.0pt;&quot;&gt;Digital media libraries&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Bynder often serves as the single source of truth for digital assets while Optimizely serves as the source of truth for content.&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: center;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h3&gt;&lt;strong&gt;Why Do We Need a Connector?&lt;/strong&gt;&lt;/h3&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Without a connector, editorial teams typically follow this process:&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Bynder DAM &lt;span style=&quot;font-family: Wingdings; mso-ascii-font-family: Aptos; mso-ascii-theme-font: minor-latin; mso-hansi-font-family: Aptos; mso-hansi-theme-font: minor-latin; mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&lt;span style=&quot;mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&amp;agrave;&lt;/span&gt;&lt;/span&gt; Download Asset &lt;span style=&quot;font-family: Wingdings; mso-ascii-font-family: Aptos; mso-ascii-theme-font: minor-latin; mso-hansi-font-family: Aptos; mso-hansi-theme-font: minor-latin; mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&lt;span style=&quot;mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&amp;agrave;&lt;/span&gt;&lt;/span&gt; Upload Asset to CMS &lt;span style=&quot;font-family: Wingdings; mso-ascii-font-family: Aptos; mso-ascii-theme-font: minor-latin; mso-hansi-font-family: Aptos; mso-hansi-theme-font: minor-latin; mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&lt;span style=&quot;mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&amp;agrave;&lt;/span&gt;&lt;/span&gt; Use Asset on Website&lt;br /&gt;OR&lt;br /&gt;Bynder DAM &lt;span style=&quot;font-family: Wingdings; mso-ascii-font-family: Aptos; mso-ascii-theme-font: minor-latin; mso-hansi-font-family: Aptos; mso-hansi-theme-font: minor-latin; mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&lt;span style=&quot;mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&amp;agrave;&lt;/span&gt;&lt;/span&gt; Copy Asset URL &lt;span style=&quot;font-family: Wingdings; mso-ascii-font-family: Aptos; mso-ascii-theme-font: minor-latin; mso-hansi-font-family: Aptos; mso-hansi-theme-font: minor-latin; mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&lt;span style=&quot;mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&amp;agrave;&lt;/span&gt;&lt;/span&gt; Paste Asset URL to CMS &lt;span style=&quot;font-family: Wingdings; mso-ascii-font-family: Aptos; mso-ascii-theme-font: minor-latin; mso-hansi-font-family: Aptos; mso-hansi-theme-font: minor-latin; mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&lt;span style=&quot;mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&amp;agrave;&lt;/span&gt;&lt;/span&gt; Use Asset on Website&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;This creates multiple problems:&lt;/p&gt;
&lt;ul style=&quot;margin-top: 0cm;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo1; tab-stops: list 36.0pt;&quot;&gt;Duplicate storage&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo1; tab-stops: list 36.0pt;&quot;&gt;Asset versioning issues&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo1; tab-stops: list 36.0pt;&quot;&gt;Governance challenges&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo1; tab-stops: list 36.0pt;&quot;&gt;Increased maintenance effort&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo1; tab-stops: list 36.0pt;&quot;&gt;Multiple sources of truth&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;The Bynder Connector changes the process significantly:&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Bynder DAM &lt;span style=&quot;font-family: Wingdings; mso-ascii-font-family: Aptos; mso-ascii-theme-font: minor-latin; mso-hansi-font-family: Aptos; mso-hansi-theme-font: minor-latin; mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&lt;span style=&quot;mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&amp;agrave;&lt;/span&gt;&lt;/span&gt; DAM Connector &lt;span style=&quot;font-family: Wingdings; mso-ascii-font-family: Aptos; mso-ascii-theme-font: minor-latin; mso-hansi-font-family: Aptos; mso-hansi-theme-font: minor-latin; mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&lt;span style=&quot;mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&amp;agrave;&lt;/span&gt;&lt;/span&gt; Optimizely Graph &amp;amp; CMS &lt;span style=&quot;font-family: Wingdings; mso-ascii-font-family: Aptos; mso-ascii-theme-font: minor-latin; mso-hansi-font-family: Aptos; mso-hansi-theme-font: minor-latin; mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&lt;span style=&quot;mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&amp;agrave;&lt;/span&gt;&lt;/span&gt; Frontend Application&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Benefits include:&lt;/p&gt;
&lt;ul style=&quot;margin-top: 0cm;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l1 level1 lfo2; tab-stops: list 36.0pt;&quot;&gt;Single source of truth&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l1 level1 lfo2; tab-stops: list 36.0pt;&quot;&gt;No duplicate assets&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l1 level1 lfo2; tab-stops: list 36.0pt;&quot;&gt;Improved governance&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l1 level1 lfo2; tab-stops: list 36.0pt;&quot;&gt;Centralized asset management&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l1 level1 lfo2; tab-stops: list 36.0pt;&quot;&gt;Better scalability&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Editors never need to download and re-upload assets.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Instead, references to Bynder assets become available directly within Optimizely CMS.&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: center;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;
&lt;h2 class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;&lt;strong&gt;Understanding the Bynder Asset Model&lt;/strong&gt;&lt;/h2&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;One of the first things I wanted to understand was what data becomes available. &lt;span style=&quot;font-size: 12.0pt; line-height: 115%; font-family: &#39;Aptos&#39;,sans-serif; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: Aptos; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: &#39;Times New Roman&#39;; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-IN; mso-fareast-language: EN-US; mso-bidi-language: AR-SA;&quot;&gt;A Bynder asset can be thought of as a combination of following:&lt;/span&gt;&lt;/p&gt;
&lt;ul class=&quot;article-editor-bullet-list&quot;&gt;
&lt;li class=&quot;article-editor-list-item&quot;&gt;
&lt;p class=&quot;article-editor-paragraph&quot; style=&quot;text-align: left;&quot;&gt;&lt;strong&gt;Bynder Managed Fields - &lt;/strong&gt;Maintained automatically by Bynder. These fields describe the physical asset itself.&lt;/p&gt;
&lt;/li&gt;
&lt;li class=&quot;article-editor-list-item&quot; style=&quot;text-align: left;&quot;&gt;
&lt;p class=&quot;article-editor-paragraph&quot;&gt;&lt;strong&gt;User Managed Fields - &lt;/strong&gt;These fields are managed by DAM users and are often used for: Search, Categorization, Filtering, Metadata enrichment.&lt;/p&gt;
&lt;/li&gt;
&lt;li class=&quot;article-editor-list-item&quot; style=&quot;text-align: left;&quot;&gt;
&lt;p class=&quot;article-editor-paragraph&quot;&gt;&lt;strong&gt;User Managed Flags: &lt;/strong&gt;These fields can influence asset behavior. These become particularly important when assets are exposed to public-facing websites.&lt;/p&gt;
&lt;/li&gt;
&lt;li class=&quot;article-editor-list-item&quot; style=&quot;text-align: left;&quot;&gt;
&lt;p class=&quot;article-editor-paragraph&quot;&gt;&lt;strong&gt;URLs:&lt;/strong&gt; Assets URLs based on asset object and various configurations and enabled features in Bynder.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; line-height: 115%; font-family: &#39;Aptos&#39;,sans-serif; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: Aptos; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: &#39;Times New Roman&#39;; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-IN; mso-fareast-language: EN-US; mso-bidi-language: AR-SA;&quot;&gt;&lt;br /&gt;&lt;img src=&quot;/link/717b08ca5a0f40aaa9d6924e17798cc1.aspx&quot; /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: center;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h3 class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;&lt;strong&gt;Understanding the Available Asset URLs&lt;/strong&gt;&lt;/h3&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;One of the most useful discoveries was understanding which URLs are available for different asset types.&amp;nbsp;Not every asset type exposes the same URLs.&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: center;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h4 class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;&lt;strong&gt;Image Assets - &lt;/strong&gt;Image assets typically provide:&lt;/h4&gt;
&lt;table class=&quot;MsoTableGrid&quot; style=&quot;border-collapse: collapse; margin-left: 0px; margin-right: auto; width: 79.5455%; height: 233px; border-image: initial; border: medium none currentcolor;&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 40.7625px;&quot;&gt;
&lt;td style=&quot;width: 33.7357%; border: 1pt solid windowtext; padding: 0cm 5.4pt; text-align: left; line-height: 1; height: 40.7625px; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&lt;strong&gt;Original URL&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 32.4977%; border-width: 1pt 1pt 1pt medium; border-style: solid solid solid none; border-color: windowtext windowtext windowtext currentcolor; border-image: initial; padding: 0cm 5.4pt; text-align: left; line-height: 1; height: 40.7625px; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0cm; line-height: normal;&quot;&gt;&lt;strong&gt;Thumbnail URL&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 33.5809%; border-width: 1pt 1pt 1pt medium; border-style: solid solid solid none; border-color: windowtext windowtext windowtext currentcolor; border-image: initial; padding: 0cm 5.4pt; text-align: left; line-height: 1; height: 40.7625px; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0cm; line-height: normal;&quot;&gt;&lt;strong&gt;Transform Base URL&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 65.2px;&quot;&gt;
&lt;td style=&quot;width: 33.7357%; border-width: medium 1pt 1pt; border-style: none solid solid; border-color: currentcolor windowtext windowtext; border-image: initial; padding: 0cm 5.4pt; text-align: left; line-height: 1; height: 65.2px; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;Represents the original uploaded file.&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 32.4977%; border-width: medium 1pt 1pt medium; border-style: none solid solid none; border-color: currentcolor windowtext windowtext currentcolor; padding: 0cm 5.4pt; text-align: left; line-height: 1; height: 65.2px; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0cm; line-height: normal;&quot;&gt;Represents a smaller version of the image.&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 33.5809%; border-width: medium 1pt 1pt medium; border-style: none solid solid none; border-color: currentcolor windowtext windowtext currentcolor; padding: 0cm 5.4pt; text-align: left; line-height: 1; height: 65.2px; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0cm; line-height: normal;&quot;&gt;Instead of generating multiple physical image versions, Bynder allows images to be transformed dynamically.&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 135.262px;&quot;&gt;
&lt;td style=&quot;width: 33.7357%; border-width: medium 1pt 1pt; border-style: none solid solid; border-color: currentcolor windowtext windowtext; border-image: initial; padding: 0cm 5.4pt; text-align: left; line-height: 1; height: 135.262px; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;Use cases:&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0cm; line-height: normal;&quot;&gt;High-resolution download, &lt;br /&gt;Print-quality rendering, &lt;br /&gt;Internal consumption&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 32.4977%; border-width: medium 1pt 1pt medium; border-style: none solid solid none; border-color: currentcolor windowtext windowtext currentcolor; padding: 0cm 5.4pt; text-align: left; line-height: 1; height: 135.262px; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;Use cases:&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;Asset pickers, &lt;br /&gt;Search results, &lt;br /&gt;Content listings, &lt;br /&gt;Media libraries&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 33.5809%; border-width: medium 1pt 1pt medium; border-style: none solid solid none; border-color: currentcolor windowtext windowtext currentcolor; padding: 0cm 5.4pt; text-align: left; line-height: 1; height: 135.262px; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;Use cases:&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;Hero banners, &lt;br /&gt;Product images, &lt;br /&gt;Responsive image delivery, &lt;br /&gt;Mobile optimization, &lt;br /&gt;Social sharing images&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;Transform Base URL helps improve:&lt;/p&gt;
&lt;ul&gt;
&lt;li style=&quot;text-align: left;&quot;&gt;Performance&lt;/li&gt;
&lt;li style=&quot;text-align: left;&quot;&gt;Page speed&lt;/li&gt;
&lt;li style=&quot;text-align: left;&quot;&gt;Bandwidth consumption&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: center;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h4 class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;&lt;strong&gt;Video Assets - &lt;/strong&gt;Video assets typically provide:&lt;/h4&gt;
&lt;table class=&quot;MsoTableGrid&quot; style=&quot;width: 948px; border-collapse: collapse; border-image: initial; height: 188px; border: medium none currentcolor;&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;mso-yfti-irow: 0; mso-yfti-firstrow: yes;&quot;&gt;
&lt;td style=&quot;width: 301.017px; border: 1pt solid windowtext; padding: 0cm 5.4pt; text-align: left; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&lt;strong&gt;Original URL&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 300.742px; border-width: 1pt 1pt 1pt medium; border-style: solid solid solid none; border-color: windowtext windowtext windowtext currentcolor; border-image: initial; padding: 0cm 5.4pt; text-align: left; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&lt;strong&gt;Thumbnail URL&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 299.905px; border-width: 1pt 1pt 1pt medium; border-style: solid solid solid none; border-color: windowtext windowtext windowtext currentcolor; border-image: initial; padding: 0cm 5.4pt; text-align: left; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0cm; line-height: normal;&quot;&gt;&lt;strong&gt;Video Preview URL&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;mso-yfti-irow: 1;&quot;&gt;
&lt;td style=&quot;width: 301.017px; border-width: medium 1pt 1pt; border-style: none solid solid; border-color: currentcolor windowtext windowtext; border-image: initial; padding: 0cm 5.4pt; text-align: left; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;The complete video asset&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 300.742px; border-width: medium 1pt 1pt medium; border-style: none solid solid none; border-color: currentcolor windowtext windowtext currentcolor; padding: 0cm 5.4pt; text-align: left; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0cm; line-height: normal;&quot;&gt;Poster image for the video.&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 299.905px; border-width: medium 1pt 1pt medium; border-style: none solid solid none; border-color: currentcolor windowtext windowtext currentcolor; padding: 0cm 5.4pt; text-align: left; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0cm; line-height: normal;&quot;&gt;Optimized preview version.&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;mso-yfti-irow: 2; mso-yfti-lastrow: yes;&quot;&gt;
&lt;td style=&quot;width: 301.017px; border-width: medium 1pt 1pt; border-style: none solid solid; border-color: currentcolor windowtext windowtext; border-image: initial; padding: 0cm 5.4pt; text-align: left; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;Use cases:&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;Full playback,&lt;br /&gt;Downloads,&lt;br /&gt;Media processing&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 300.742px; border-width: medium 1pt 1pt medium; border-style: none solid solid none; border-color: currentcolor windowtext windowtext currentcolor; padding: 0cm 5.4pt; text-align: left; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;Use cases:&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;Video cards,&lt;br /&gt;Search results,&lt;br /&gt;Carousel view&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 299.905px; border-width: medium 1pt 1pt medium; border-style: none solid solid none; border-color: currentcolor windowtext windowtext currentcolor; padding: 0cm 5.4pt; text-align: left; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;Use cases:&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0cm; line-height: normal;&quot;&gt;Video galleries,&lt;br /&gt;Content previews,&lt;br /&gt;Editorial review&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: center;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h4 class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;&lt;strong&gt;Document Assets - &lt;/strong&gt;Document assets typically provide:&lt;/h4&gt;
&lt;table class=&quot;MsoTableGrid&quot; style=&quot;width: 584px; border-collapse: collapse; border-image: initial; height: 177px; border: medium none currentcolor;&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;mso-yfti-irow: 0; mso-yfti-firstrow: yes;&quot;&gt;
&lt;td style=&quot;width: 276.225px; border: 1pt solid windowtext; padding: 0cm 5.4pt; text-align: left; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&lt;strong&gt;Original URL&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 276.238px; border-width: 1pt 1pt 1pt medium; border-style: solid solid solid none; border-color: windowtext windowtext windowtext currentcolor; border-image: initial; padding: 0cm 5.4pt; text-align: left; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&lt;strong&gt;Thumbnail URL&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;mso-yfti-irow: 1; mso-yfti-lastrow: yes;&quot;&gt;
&lt;td style=&quot;width: 276.225px; border-width: medium 1pt 1pt; border-style: none solid solid; border-color: currentcolor windowtext windowtext; border-image: initial; padding: 0cm 5.4pt; text-align: left; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;Used for:&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;Document downloads,&lt;br /&gt;Direct viewing,&lt;br /&gt;PDF rendering&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 276.238px; border-width: medium 1pt 1pt medium; border-style: none solid solid none; border-color: currentcolor windowtext windowtext currentcolor; padding: 0cm 5.4pt; text-align: left; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;Used for:&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;Document previews,&lt;br /&gt;Search results,&lt;br /&gt;Asset browsers&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: center;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h2 class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;&lt;strong&gt;Setting Up the Connector&lt;/strong&gt;&lt;/h2&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;Connector setup is relatively straightforward. Follow the documentation here:&lt;br /&gt;&lt;a href=&quot;https://support.optimizely.com/hc/en-us/articles/37465644973069-Configure-the-Bynder-app&quot;&gt;https://support.optimizely.com/hc/en-us/articles/37465644973069-Configure-the-Bynder-app&lt;/a&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;Typical configuration requires:&lt;/p&gt;
&lt;ol&gt;
&lt;li style=&quot;text-align: left;&quot;&gt;&lt;!-- [if !supportLists]--&gt;Bynder Admin Access&lt;/li&gt;
&lt;li style=&quot;text-align: left;&quot;&gt;&lt;!--[endif]--&gt;Webhook permissions on your Bynder user profile to authorize this integration.&lt;br /&gt;&lt;span style=&quot;mso-no-proof: yes;&quot;&gt;&lt;img src=&quot;/link/9c05c0fedea141afa9dab9a3c5c06c34.aspx&quot; width=&quot;333&quot; height=&quot;338&quot; border=&quot;0&quot; /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;mso-no-proof: yes;&quot;&gt;&lt;span style=&quot;text-indent: -18pt;&quot;&gt;Bynder OAuth App, Client ID, Client Secret&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;text-indent: -18pt;&quot;&gt;OCP (Connect Platform) access in Optimizely SaaS instance.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;Note that:&lt;br /&gt;a) Setup separate sync for:&lt;/p&gt;
&lt;p class=&quot;MsoListParagraphCxSpFirst&quot; style=&quot;margin-left: 54pt; text-indent: -18pt; text-align: left;&quot;&gt;&lt;!-- [if !supportLists]--&gt;&lt;span style=&quot;mso-bidi-font-family: Aptos; mso-bidi-theme-font: minor-latin;&quot;&gt;&lt;span style=&quot;mso-list: Ignore;&quot;&gt;1.&lt;span style=&quot;font: 7.0pt &#39;Times New Roman&#39;;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;!--[endif]--&gt;Each Bynder object i.e. image, document, video.&lt;/p&gt;
&lt;p class=&quot;MsoListParagraphCxSpMiddle&quot; style=&quot;margin-left: 54pt; text-indent: -18pt; text-align: left;&quot;&gt;&lt;!-- [if !supportLists]--&gt;&lt;span style=&quot;mso-bidi-font-family: Aptos; mso-bidi-theme-font: minor-latin;&quot;&gt;&lt;span style=&quot;mso-list: Ignore;&quot;&gt;2.&lt;span style=&quot;font: 7.0pt &#39;Times New Roman&#39;;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;!--[endif]--&gt;Each Optimizely environment i.e. Dev, Stage, Prod (test1, test2, Production)&lt;/p&gt;
&lt;p class=&quot;MsoListParagraphCxSpLast&quot; style=&quot;margin-left: 54pt; text-indent: -18pt; text-align: left;&quot;&gt;&lt;!-- [if !supportLists]--&gt;&lt;span style=&quot;mso-bidi-font-family: Aptos; mso-bidi-theme-font: minor-latin;&quot;&gt;&lt;span style=&quot;mso-list: Ignore;&quot;&gt;3.&lt;span style=&quot;font: 7.0pt &#39;Times New Roman&#39;;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;!--[endif]--&gt;Each Language (if applicable) otherwise use Neutral language&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;b) &lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&lt;/span&gt;In the Destination create separate ContentType for each object i.e. Image &lt;span style=&quot;font-family: Wingdings; mso-ascii-font-family: Aptos; mso-ascii-theme-font: minor-latin; mso-hansi-font-family: Aptos; mso-hansi-theme-font: minor-latin; mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&lt;span style=&quot;mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&amp;agrave;&lt;/span&gt;&lt;/span&gt; BynderImage, Document &lt;span style=&quot;font-family: Wingdings; mso-ascii-font-family: Aptos; mso-ascii-theme-font: minor-latin; mso-hansi-font-family: Aptos; mso-hansi-theme-font: minor-latin; mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&lt;span style=&quot;mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&amp;agrave;&lt;/span&gt;&lt;/span&gt; BynderDocument, Video &lt;span style=&quot;font-family: Wingdings; mso-ascii-font-family: Aptos; mso-ascii-theme-font: minor-latin; mso-hansi-font-family: Aptos; mso-hansi-theme-font: minor-latin; mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&lt;span style=&quot;mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&amp;agrave;&lt;/span&gt;&lt;/span&gt; BynderVideo&lt;br /&gt;&lt;span style=&quot;mso-no-proof: yes;&quot;&gt;&lt;!-- [if gte vml 1]&gt;&lt;v:shape id=&quot;Picture_x0020_1&quot;
 o:spid=&quot;_x0000_i1027&quot; type=&quot;#_x0000_t75&quot; style=&#39;width:141.6pt;height:142.8pt;
 visibility:visible;mso-wrap-style:square&#39;&gt;
 &lt;v:imagedata src=&quot;file:///C:/Users/VBanka/AppData/Local/Temp/msohtmlclip1/01/clip_image003.png&quot;
  o:title=&quot;&quot;/&gt;
&lt;/v:shape&gt;&lt;![endif]--&gt;&lt;!-- [if !vml]--&gt;&lt;img src=&quot;/link/f5f31192a9884124b0db9dd9fd09fd70.aspx&quot; width=&quot;240&quot; height=&quot;241&quot; border=&quot;0&quot; /&gt;&lt;br /&gt;&lt;br /&gt;&lt;!--[endif]--&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;c) Ensure selection of &amp;ldquo;Make available as CMS Content&amp;rdquo; checkbox.&lt;br /&gt;&lt;span style=&quot;mso-no-proof: yes;&quot;&gt;&lt;!-- [if gte vml 1]&gt;&lt;v:shape id=&quot;Picture_x0020_18&quot;
 o:spid=&quot;_x0000_i1026&quot; type=&quot;#_x0000_t75&quot; style=&#39;width:277.8pt;height:293.4pt;
 visibility:visible;mso-wrap-style:square&#39;&gt;
 &lt;v:imagedata src=&quot;file:///C:/Users/VBanka/AppData/Local/Temp/msohtmlclip1/01/clip_image005.png&quot;
  o:title=&quot;&quot;/&gt;
&lt;/v:shape&gt;&lt;![endif]--&gt;&lt;!-- [if !vml]--&gt;&lt;img src=&quot;/link/282149262aa94df1b3376c4a82e1ac77.aspx&quot; width=&quot;370&quot; height=&quot;391&quot; border=&quot;0&quot; /&gt;&lt;br /&gt;&lt;br /&gt;&lt;!--[endif]--&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;Once configured successfully:&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;Bynder Assets &lt;span style=&quot;font-family: Wingdings; mso-ascii-font-family: Aptos; mso-ascii-theme-font: minor-latin; mso-hansi-font-family: Aptos; mso-hansi-theme-font: minor-latin; mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&lt;span style=&quot;mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&amp;agrave;&lt;/span&gt;&lt;/span&gt; DAM Connector &lt;span style=&quot;font-family: Wingdings; mso-ascii-font-family: Aptos; mso-ascii-theme-font: minor-latin; mso-hansi-font-family: Aptos; mso-hansi-theme-font: minor-latin; mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&lt;span style=&quot;mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&amp;agrave;&lt;/span&gt;&lt;/span&gt; Optimizely Graph + CMS Content Type&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;Assets become available inside Optimizely Graph and CMS both without custom code.&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h2 class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;&lt;strong&gt;Content Manager Experience&lt;/strong&gt;&lt;/h2&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;From an editorial perspective, this is where things become interesting.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;After configuration, editors can browse DAM assets directly inside CMS.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;The Content Manager navigation typically exposes a dedicated content provider area.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;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_20&quot; o:spid=&quot;_x0000_i1052&quot; type=&quot;#_x0000_t75&quot;
 style=&#39;width:451.2pt;height:151.8pt;visibility:visible;mso-wrap-style:square&#39;
 o:bordertopcolor=&quot;yellow pure&quot; o:borderleftcolor=&quot;yellow pure&quot;
 o:borderbottomcolor=&quot;yellow pure&quot; o:borderrightcolor=&quot;yellow pure&quot;&gt;
 &lt;v:imagedata src=&quot;file:///C:/Users/VBanka/AppData/Local/Temp/msohtmlclip1/01/clip_image001.png&quot;
  o:title=&quot;&quot;/&gt;
 &lt;w:bordertop type=&quot;single&quot; width=&quot;6&quot;/&gt;
 &lt;w:borderleft type=&quot;single&quot; width=&quot;6&quot;/&gt;
 &lt;w:borderbottom type=&quot;single&quot; width=&quot;6&quot;/&gt;
 &lt;w:borderright type=&quot;single&quot; width=&quot;6&quot;/&gt;
&lt;/v:shape&gt;&lt;![endif]--&gt;&lt;!-- [if !vml]--&gt;&lt;img src=&quot;/link/470118ee057e4cdcb0fbfd2a052e02bb.aspx&quot; width=&quot;603&quot; height=&quot;204&quot; /&gt;&lt;!--[endif]--&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;mso-no-proof: yes;&quot;&gt;&lt;!-- [if gte vml 1]&gt;&lt;v:shape
 id=&quot;Picture_x0020_1&quot; o:spid=&quot;_x0000_i1051&quot; type=&quot;#_x0000_t75&quot; style=&#39;width:451.2pt;
 height:169.8pt;visibility:visible;mso-wrap-style:square&#39; o:bordertopcolor=&quot;yellow pure&quot;
 o:borderleftcolor=&quot;yellow pure&quot; o:borderbottomcolor=&quot;yellow pure&quot;
 o:borderrightcolor=&quot;yellow pure&quot;&gt;
 &lt;v:imagedata src=&quot;file:///C:/Users/VBanka/AppData/Local/Temp/msohtmlclip1/01/clip_image003.png&quot;
  o:title=&quot;&quot;/&gt;
 &lt;w:bordertop type=&quot;single&quot; width=&quot;6&quot;/&gt;
 &lt;w:borderleft type=&quot;single&quot; width=&quot;6&quot;/&gt;
 &lt;w:borderbottom type=&quot;single&quot; width=&quot;6&quot;/&gt;
 &lt;w:borderright type=&quot;single&quot; width=&quot;6&quot;/&gt;
&lt;/v:shape&gt;&lt;![endif]--&gt;&lt;!-- [if !vml]--&gt;&lt;img src=&quot;/link/aaa2751bab514ace865a38bbd7c8ab7d.aspx&quot; width=&quot;603&quot; height=&quot;228&quot; /&gt;&lt;!--[endif]--&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;At this point, editors do not need to leave CMS to discover assets stored in Bynder.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;They can:&amp;nbsp;Search, Browse, Filter, Reference assets&amp;nbsp;directly from the content authoring experience.&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h2 class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;&lt;strong&gt;How Assets Appear in Optimizely Graph&lt;/strong&gt;&lt;/h2&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;Once synchronized, DAM assets become queryable through Graph.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;A simplified query may look like (the exact query will depend on your field mappings)&lt;br style=&quot;mso-special-character: line-break;&quot; /&gt;&lt;!-- [if !supportLineBreakNewLine]--&gt;&lt;br style=&quot;mso-special-character: line-break;&quot; /&gt;&lt;!--[endif]--&gt;&lt;/p&gt;
&lt;table class=&quot;MsoTableGrid&quot; style=&quot;border-collapse: collapse; margin-left: 0px; margin-right: auto; border-image: initial; height: 362px; width: 73.2839%; border: medium none currentcolor;&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;mso-yfti-irow: 0; mso-yfti-firstrow: yes;&quot;&gt;
&lt;td style=&quot;width: 0%; border: 1pt solid windowtext; padding: 0cm 5.4pt; text-align: left; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0cm; line-height: normal;&quot;&gt;&lt;strong&gt;Image&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 0%; border-width: 1pt 1pt 1pt medium; border-style: solid solid solid none; border-color: windowtext windowtext windowtext currentcolor; border-image: initial; padding: 0cm 5.4pt; text-align: left; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0cm; line-height: normal;&quot;&gt;&lt;strong&gt;Video&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 0%; border-width: 1pt 1pt 1pt medium; border-style: solid solid solid none; border-color: windowtext windowtext windowtext currentcolor; border-image: initial; padding: 0cm 5.4pt; text-align: left; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0cm; line-height: normal;&quot;&gt;&lt;strong&gt;Document&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;mso-yfti-irow: 1; mso-yfti-lastrow: yes;&quot;&gt;
&lt;td style=&quot;width: 0%; border-width: medium 1pt 1pt; border-style: none solid solid; border-color: currentcolor windowtext windowtext; border-image: initial; padding: 0cm 5.4pt; text-align: left; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;query {&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&amp;nbsp; BynderImage {&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; items {&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; id&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0cm; line-height: normal;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; name&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; originalUrl&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; thumbnailUrl&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; transformBaseUrl&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&amp;nbsp; }&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0cm; line-height: normal;&quot;&gt;}&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 0%; border-width: medium 1pt 1pt medium; border-style: none solid solid none; border-color: currentcolor windowtext windowtext currentcolor; padding: 0cm 5.4pt; text-align: left; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;query {&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&amp;nbsp; BynderVideo {&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; items {&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; id&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0cm; line-height: normal;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; name&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; originalUrl&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; thumbnailUrl&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;videoPreviewURL&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&amp;nbsp; }&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0cm; line-height: normal;&quot;&gt;}&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 0%; border-width: medium 1pt 1pt medium; border-style: none solid solid none; border-color: currentcolor windowtext windowtext currentcolor; padding: 0cm 5.4pt; text-align: left; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;query {&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&amp;nbsp; BynderDocument {&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; items {&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; id&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0cm; line-height: normal;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; name&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; originalUrl&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; thumbnailUrl&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&amp;nbsp; }&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0cm; line-height: normal;&quot;&gt;}&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;One of the biggest advantages is that content and media can now be retrieved through a single API layer.&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h2 class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;&lt;strong&gt;Using DAM Assets in Content Types&lt;/strong&gt;&lt;/h2&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;The connector enables DAM-specific property types.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;Example:&lt;/p&gt;
&lt;table class=&quot;MsoTableGrid&quot; style=&quot;border-collapse: collapse; border-image: initial; height: 203px; width: 67.718%; border: medium none currentcolor;&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;mso-yfti-irow: 0; mso-yfti-firstrow: yes; mso-yfti-lastrow: yes;&quot;&gt;
&lt;td style=&quot;width: 0%; border: 1pt solid windowtext; padding: 0cm 5.4pt; text-align: left; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;News Article Page&lt;/p&gt;
&lt;p class=&quot;MsoListParagraphCxSpFirst&quot; style=&quot;mso-add-space: auto; text-indent: -18.0pt; line-height: normal; mso-list: l0 level1 lfo1; margin: 0cm 0cm 0cm 54.0pt;&quot;&gt;&lt;!-- [if !supportLists]--&gt;&lt;span style=&quot;mso-ascii-font-family: Aptos; mso-fareast-font-family: Aptos; mso-hansi-font-family: Aptos; mso-bidi-font-family: Aptos;&quot;&gt;&lt;span style=&quot;mso-list: Ignore;&quot;&gt;-&lt;span style=&quot;font: 7.0pt &#39;Times New Roman&#39;;&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;/span&gt;&lt;!--[endif]--&gt;Headline&lt;/p&gt;
&lt;p class=&quot;MsoListParagraphCxSpMiddle&quot; style=&quot;mso-add-space: auto; text-indent: -18.0pt; line-height: normal; mso-list: l0 level1 lfo1; margin: 0cm 0cm 0cm 54.0pt;&quot;&gt;&lt;!-- [if !supportLists]--&gt;&lt;span style=&quot;mso-ascii-font-family: Aptos; mso-fareast-font-family: Aptos; mso-hansi-font-family: Aptos; mso-bidi-font-family: Aptos;&quot;&gt;&lt;span style=&quot;mso-list: Ignore;&quot;&gt;-&lt;span style=&quot;font: 7.0pt &#39;Times New Roman&#39;;&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;/span&gt;&lt;!--[endif]--&gt;Summary&lt;/p&gt;
&lt;p class=&quot;MsoListParagraphCxSpLast&quot; style=&quot;mso-add-space: auto; text-indent: -18.0pt; line-height: normal; mso-list: l0 level1 lfo1; margin: 0cm 0cm 0cm 54.0pt;&quot;&gt;&lt;!-- [if !supportLists]--&gt;&lt;span style=&quot;mso-ascii-font-family: Aptos; mso-fareast-font-family: Aptos; mso-hansi-font-family: Aptos; mso-bidi-font-family: Aptos;&quot;&gt;&lt;span style=&quot;mso-list: Ignore;&quot;&gt;-&lt;span style=&quot;font: 7.0pt &#39;Times New Roman&#39;;&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;/span&gt;&lt;!--[endif]--&gt;Hero Image&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;Where:&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;Hero Image&amp;nbsp;is a Bynder Image reference.&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 0%; border-width: 1pt 1pt 1pt medium; border-style: solid solid solid none; border-color: windowtext windowtext windowtext currentcolor; border-image: initial; padding: 0cm 5.4pt; text-align: left; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;Product Page&lt;/p&gt;
&lt;p class=&quot;MsoListParagraphCxSpFirst&quot; style=&quot;mso-add-space: auto; text-indent: -18.0pt; line-height: normal; mso-list: l0 level1 lfo1; margin: 0cm 0cm 0cm 54.0pt;&quot;&gt;&lt;!-- [if !supportLists]--&gt;&lt;span style=&quot;mso-ascii-font-family: Aptos; mso-fareast-font-family: Aptos; mso-hansi-font-family: Aptos; mso-bidi-font-family: Aptos;&quot;&gt;&lt;span style=&quot;mso-list: Ignore;&quot;&gt;-&lt;span style=&quot;font: 7.0pt &#39;Times New Roman&#39;;&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;/span&gt;&lt;!--[endif]--&gt;Title&lt;/p&gt;
&lt;p class=&quot;MsoListParagraphCxSpMiddle&quot; style=&quot;mso-add-space: auto; text-indent: -18.0pt; line-height: normal; mso-list: l0 level1 lfo1; margin: 0cm 0cm 0cm 54.0pt;&quot;&gt;&lt;!-- [if !supportLists]--&gt;&lt;span style=&quot;mso-ascii-font-family: Aptos; mso-fareast-font-family: Aptos; mso-hansi-font-family: Aptos; mso-bidi-font-family: Aptos;&quot;&gt;&lt;span style=&quot;mso-list: Ignore;&quot;&gt;-&lt;span style=&quot;font: 7.0pt &#39;Times New Roman&#39;;&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;/span&gt;&lt;!--[endif]--&gt;Product Image&lt;/p&gt;
&lt;p class=&quot;MsoListParagraphCxSpMiddle&quot; style=&quot;mso-add-space: auto; text-indent: -18.0pt; line-height: normal; mso-list: l0 level1 lfo1; margin: 0cm 0cm 0cm 54.0pt;&quot;&gt;&lt;!-- [if !supportLists]--&gt;&lt;span style=&quot;mso-ascii-font-family: Aptos; mso-fareast-font-family: Aptos; mso-hansi-font-family: Aptos; mso-bidi-font-family: Aptos;&quot;&gt;&lt;span style=&quot;mso-list: Ignore;&quot;&gt;-&lt;span style=&quot;font: 7.0pt &#39;Times New Roman&#39;;&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;/span&gt;&lt;!--[endif]--&gt;Product Video&lt;/p&gt;
&lt;p class=&quot;MsoListParagraphCxSpLast&quot; style=&quot;mso-add-space: auto; text-indent: -18.0pt; line-height: normal; mso-list: l0 level1 lfo1; margin: 0cm 0cm 0cm 54.0pt;&quot;&gt;&lt;!-- [if !supportLists]--&gt;&lt;span style=&quot;mso-ascii-font-family: Aptos; mso-fareast-font-family: Aptos; mso-hansi-font-family: Aptos; mso-bidi-font-family: Aptos;&quot;&gt;&lt;span style=&quot;mso-list: Ignore;&quot;&gt;-&lt;span style=&quot;font: 7.0pt &#39;Times New Roman&#39;;&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;/span&gt;&lt;!--[endif]--&gt;Product Brochure&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;Where:&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0cm; line-height: normal;&quot;&gt;Each asset property can reference a different Bynder asset type i.e. BynderImage, BynderVideo, and BynderDocument&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h3 class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;&lt;strong&gt;Frontend Consumption&lt;/strong&gt;&lt;/h3&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;Once data is available through Graph, frontend applications can retrieve and render it.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;Example:&lt;/p&gt;
&lt;table class=&quot;MsoTableGrid&quot; style=&quot;border-collapse: collapse; margin-left: 0px; margin-right: auto; border-image: initial; height: 714px; width: 76.9944%; border: medium none currentcolor;&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;mso-yfti-irow: 0; mso-yfti-firstrow: yes; mso-yfti-lastrow: yes;&quot;&gt;
&lt;td style=&quot;width: 0%; border: 1pt solid windowtext; padding: 0cm 5.4pt; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;query {&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp; NewsArticle {&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; items {&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; headline&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; heroImage {&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; name&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; thumbnailUrl&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; originalUrl&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; transformBaseUrl&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp; }&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;}&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 0%; border: 1pt solid windowtext; padding: 0cm 5.4pt; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;query {&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp; Product {&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; items {&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; title&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; productImage {&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; name&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; thumbnailUrl&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; originalUrl&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; transformBaseUrl&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; productVideo {&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; name&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; thumbnailUrl&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; originalUrl&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; videoPreviewUrl&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; productBrochure{&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; name&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; thumbnailUrl&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; originalUrl&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;&amp;nbsp; }&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8pt; line-height: 115%; text-align: left;&quot;&gt;}&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;
&lt;h3&gt;&lt;strong&gt;Asset Rendering:&lt;/strong&gt;&lt;/h3&gt;
&lt;/div&gt;
&lt;table class=&quot;MsoTableGrid&quot; style=&quot;border-collapse: collapse; margin-left: 0px; margin-right: auto; width: 75.9895%; border-image: initial; height: 186px; border: medium none currentcolor;&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;mso-yfti-irow: 0; mso-yfti-firstrow: yes;&quot;&gt;
&lt;td style=&quot;width: 30.8527%; border: 1pt solid windowtext; padding: 0cm 5.4pt; text-align: left; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0cm; line-height: normal;&quot;&gt;&lt;strong&gt;Rendering Images&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 33.8999%; border-width: 1pt 1pt 1pt medium; border-style: solid solid solid none; border-color: windowtext windowtext windowtext currentcolor; border-image: initial; padding: 0cm 5.4pt; text-align: left; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0cm; line-height: normal;&quot;&gt;&lt;strong&gt;Rendering Videos&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 35.227%; border-width: 1pt 1pt 1pt medium; border-style: solid solid solid none; border-color: windowtext windowtext windowtext currentcolor; border-image: initial; padding: 0cm 5.4pt; text-align: left; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0cm; line-height: normal;&quot;&gt;&lt;strong&gt;Rendering Documents&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;mso-yfti-irow: 1; mso-yfti-lastrow: yes;&quot;&gt;
&lt;td style=&quot;width: 30.8527%; border-width: medium 1pt 1pt; border-style: none solid solid; border-color: currentcolor windowtext windowtext; border-image: initial; padding: 0cm 5.4pt; text-align: left; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0cm; line-height: normal;&quot;&gt;&lt;strong&gt;Basic rendering:&lt;/strong&gt;&lt;br /&gt;&amp;lt;img src=&quot;{thumbnailUrl}&quot; /&amp;gt;&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0cm; 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;margin-bottom: 0cm; line-height: normal;&quot;&gt;&lt;br /&gt;&lt;strong&gt;Responsive Rendering:&lt;/strong&gt;&lt;br /&gt;&amp;lt;img&lt;br /&gt;src=&quot;{transformBaseUrl}?width=200&quot; &lt;br /&gt;/&amp;gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 33.8999%; border-width: medium 1pt 1pt medium; border-style: none solid solid none; border-color: currentcolor windowtext windowtext currentcolor; padding: 0cm 5.4pt; text-align: left; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&lt;strong&gt;Video example:&lt;/strong&gt;&lt;br /&gt;&amp;lt;video controls&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;lt;source src=&quot;{originalUrl}&quot; /&amp;gt;&lt;br /&gt;&amp;lt;/video&amp;gt;&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: center;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0cm; line-height: normal;&quot;&gt;&lt;strong&gt;Video Poster:&lt;br /&gt;&lt;/strong&gt;&amp;lt;img src=&quot;{thumbnailUrl}&quot; /&amp;gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 35.227%; border-width: medium 1pt 1pt medium; border-style: none solid solid none; border-color: currentcolor windowtext windowtext currentcolor; padding: 0cm 5.4pt; text-align: left; vertical-align: top;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&lt;strong&gt;PDF example:&lt;/strong&gt;&lt;br /&gt;&amp;lt;a href=&quot;{originalUrl}&quot;&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; Download PDF&lt;br /&gt;&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 0cm; 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;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&lt;strong&gt;Preview example:&lt;br /&gt;&lt;/strong&gt;&amp;lt;img src=&quot;{thumbnailUrl}&quot; /&amp;gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h2 class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;An ideal worflow (Bynder &lt;span style=&quot;font-size: 12.0pt; line-height: 115%; font-family: Wingdings; mso-ascii-font-family: Aptos; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: Aptos; mso-fareast-theme-font: minor-latin; mso-hansi-font-family: Aptos; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: &#39;Times New Roman&#39;; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-IN; mso-fareast-language: EN-US; mso-bidi-language: AR-SA; mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&lt;span style=&quot;mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&amp;agrave;&lt;/span&gt;&lt;/span&gt;&amp;nbsp;Optimizely &lt;span style=&quot;font-size: 12.0pt; line-height: 115%; font-family: Wingdings; mso-ascii-font-family: Aptos; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: Aptos; mso-fareast-theme-font: minor-latin; mso-hansi-font-family: Aptos; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: &#39;Times New Roman&#39;; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-IN; mso-fareast-language: EN-US; mso-bidi-language: AR-SA; mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&lt;span style=&quot;mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&amp;agrave;&lt;/span&gt;&lt;/span&gt;&amp;nbsp;Frontend application):&lt;/h2&gt;
&lt;ol&gt;
&lt;li style=&quot;text-align: left;&quot;&gt;Asset will be uploaded into Bynder along with metadata.&lt;/li&gt;
&lt;li style=&quot;text-align: left;&quot;&gt;Asset will be marked as Public in Bynder (if asset must be available using a public URL).&lt;/li&gt;
&lt;li style=&quot;text-align: left;&quot;&gt;OCP Connector will sync the asset inforamtion into Optimizely Graph.&lt;/li&gt;
&lt;li style=&quot;text-align: left;&quot;&gt;Authors in Optimizely SaaS CMS will be able to ses/select the asset inside the content manager/content items for referencing on relevant fields.&lt;/li&gt;
&lt;li style=&quot;text-align: left;&quot;&gt;Content Items will be published in Optimizely SaaS CMS.&lt;/li&gt;
&lt;li style=&quot;text-align: left;&quot;&gt;Frontend application (nextjs) will retrieve the page item content along with asset URLs using Graph query.&lt;/li&gt;
&lt;li style=&quot;text-align: left;&quot;&gt;Appropriate URL of asset will be used to render/retrieve the asset infroamtion in the application/browser.&lt;/li&gt;
&lt;li style=&quot;text-align: left;&quot;&gt;If frontend application needs asset information that is not synced in Optimizely but available in Bynder then frontend application will use Bynder APIs to retrieve the information.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h2 class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;&lt;strong&gt;Public vs Private Assets&lt;/strong&gt;&lt;/h2&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;One area that deserves special attention is asset visibility.&lt;/p&gt;
&lt;table class=&quot;MsoTableGrid&quot; style=&quot;border-collapse: collapse; width: 67.1769%; height: 327px; border-image: initial; border: medium none currentcolor;&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 308.862px;&quot;&gt;
&lt;td style=&quot;width: 50.0644%; border: 1pt solid windowtext; padding: 0cm 5.4pt; text-align: left; vertical-align: top; height: 308.862px;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&lt;strong&gt;Public Assets&lt;/strong&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;IsPublic = true&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;Typically means:&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;line-height: 115%;&quot;&gt;Frontend can access asset directly.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;line-height: 115%;&quot;&gt;No authentication required.&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 50.0644%; border-width: 1pt 1pt 1pt medium; border-style: solid solid solid none; border-color: windowtext windowtext windowtext currentcolor; border-image: initial; padding: 0cm 5.4pt; text-align: left; vertical-align: top; height: 308.862px;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;&lt;strong&gt;Non-Public Assets&lt;/strong&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;IsPublic = false&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;Potential implications:&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;line-height: 115%;&quot;&gt;Requires authentication&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;line-height: 115%;&quot;&gt;Requires authorization&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;line-height: 115%;&quot;&gt;URL will not be accessible publicly&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%;&quot;&gt;This becomes extremely important for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Extranets&lt;/li&gt;
&lt;li&gt;Employee portals&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;margin-bottom: 8.0pt; line-height: 115%; mso-list: l0 level1 lfo1; tab-stops: list 36.0pt;&quot;&gt;Restricted content&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;margin-bottom: 8pt; line-height: 115%;&quot;&gt;Use &lt;a href=&quot;https://bynder-api-documentation.readme.io/reference/get_api-v4-media-id-download&quot;&gt;Retrieve asset download&lt;/a&gt; location from Bynder API to get the URL to dowlaod the asset item.&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h2 class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;&lt;strong&gt;Things I Learned&lt;/strong&gt;&lt;/h2&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;After exploring Bynder and the connector, several observations stood out.&lt;/p&gt;
&lt;ul&gt;
&lt;li style=&quot;text-align: left;&quot;&gt;&lt;strong&gt;Not every asset type exposes the same URLs&lt;br /&gt;&lt;/strong&gt;Images typically expose more transformation capabilities than videos or documents.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot; align=&quot;center&quot;&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Transform Base URL is incredibly useful&lt;br /&gt;&lt;/strong&gt;The Transform Base URL alone can eliminate the need for multiple image renditions.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot; align=&quot;center&quot;&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Public vs Private matters early&lt;br /&gt;&lt;/strong&gt;Teams should decide asset visibility strategy before building the frontend application.&lt;br /&gt;Changing this later can require rework.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h2 class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;&lt;strong&gt;Things to Validate Early&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;&lt;strong&gt;Metadata Availability&lt;/strong&gt;
&lt;ul&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;Not every Bynder field is necessarily synchronized.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;Always validate:
&lt;ul&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;Which fields are available?&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;Which fields are searchable?&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;Which fields appear in Graph?&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;&lt;strong&gt;Filtering of Assets&lt;/strong&gt;
&lt;ul&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;You may not need to have all assets in Optimizely. Ideally Assets used on your website or target channel must be synced but rest assets should be avoided.&lt;br /&gt;Read the article &lt;a href=&quot;/link/30275ac47c4843148eeda8b7d6ed282a.aspx&quot;&gt;[Planning Your Bynder DAM and Optimizely SaaS CMS Integration the Right Way: Avoiding Asset Sprawl and Unnecessary Synchronization]&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;&lt;strong&gt;Private Asset Consumption&lt;/strong&gt;
&lt;ul&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;Private assets often introduce additional frontend delivery considerations.&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;Validate architecture before implementation.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot; align=&quot;center&quot;&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Transformation Support&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Image transformations are powerful.&lt;/li&gt;
&lt;li&gt;Equivalent functionality may not exist for every asset type.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot; align=&quot;center&quot;&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Connector Scope&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;The connector solves many DAM scenarios, but not every possible Bynder feature, or workflow may be surfaced through the OOTB connector.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Language Translation&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Bynder does not have concept of language version of asset items hence if you need to store language specific content in your assets metadata properties then figure out where that content will be stored and how the translation workflow will be executed. One of the options to keep language specific content inside Bynder is to create language specific properties e.g. Title_en, Title_fr etc. These properties can then be read through the &lt;a href=&quot;https://bynder-api-documentation.readme.io/reference/get_api-v4-media-id&quot;&gt;Retrieve specific asset&lt;/a&gt; endpoint.&lt;/li&gt;
&lt;li&gt;Also note that&amp;nbsp; Bynder allows to set the language specific labels for the options inside single select and multi-select properties. But when you retrieve assets information using the API it only returns the default language labels. In case you need language specific labels in your frontend application then you need to get all metadata properties using &lt;a href=&quot;https://bynder-api-documentation.readme.io/reference/get_api-v4-metaproperties&quot;&gt;Retrieve metaproperties&lt;/a&gt; API endpoint.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h2 class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;&lt;strong&gt;Frequently Asked Questions&lt;/strong&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li style=&quot;text-align: left;&quot;&gt;&lt;strong&gt;Does the connector synchronize custom metadata fields?&lt;br /&gt;&lt;/strong&gt;All text type metadata fields in Bynder are supposed to be exposed through synchronization but actual synchronization depends on the connector configuration. &lt;br /&gt;Always validate available fields before finalizing content models.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li style=&quot;text-align: left;&quot;&gt;&lt;strong&gt;Can filtering of assets be set during sync configuration?&lt;br /&gt;&lt;/strong&gt;OOTB connector does not support filtering; it is developed to sync all assets.&lt;br /&gt;&lt;span class=&quot;far4bl _667h42&quot;&gt;The connector itself has no filtering UI, but selective synchronization can be achieved through Bynder&#39;s permission profiles and metadata visibility rules &amp;mdash; see &lt;a href=&quot;/link/30275ac47c4843148eeda8b7d6ed282a.aspx&quot;&gt;[Planning Your Bynder DAM and Optimizely SaaS CMS Integration the Right Way: Avoiding Asset Sprawl and Unnecessary Synchronization]&lt;/a&gt;.&lt;/span&gt;&lt;br /&gt;Or Using the Bynder API custom connector can be developed to support filtering and other required features based on your business needs.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li style=&quot;text-align: left;&quot;&gt;&lt;!-- [if !supportLists]--&gt;&lt;strong&gt;Can I call Bynder APIs directly from my frontend application?&lt;br /&gt;&lt;/strong&gt;Absolutely. A common pattern is:&amp;nbsp;DAM Connector + Bynder APIs.&amp;nbsp;&lt;br /&gt;The connector handles asset references while custom APIs handle advanced functionality.&lt;br /&gt;Use &lt;a href=&quot;https://bynder-api-documentation.readme.io/reference/get_api-v4-media-id&quot;&gt;Retrieve specific asset&lt;/a&gt; endpoint from Bynder API to retrieve metadata and URLs (including custom metadata) for a specific assets.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li style=&quot;text-align: left;&quot;&gt;&lt;!-- [if !supportLists]--&gt;&lt;strong&gt;What if information is not synchronized?&lt;br /&gt;&lt;/strong&gt;Retrieve it directly from Bynder APIs. Examples: Workflow Status, Usage Rights, Approval State, Custom Metadata.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li style=&quot;text-align: left;&quot;&gt;&lt;!-- [if !supportLists]--&gt;&lt;strong&gt;Can frontend applications retrieve private assets?&lt;br /&gt;&lt;/strong&gt;It depends on the DAM security model and implementation approach.&amp;nbsp;&lt;br /&gt;Possible solutions include: Authentication, Proxy APIs, Token-based access.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h2 class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;&lt;strong&gt;Final Thoughts&lt;/strong&gt;&lt;/h2&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;The Bynder DAM Connector provides a clean bridge between enterprise asset management and modern headless content delivery.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;The most important takeaway for me was that the connector is not simply an asset picker. It establishes a relationship between:&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;Bynder DAM &lt;span style=&quot;font-family: Wingdings; mso-ascii-font-family: Aptos; mso-ascii-theme-font: minor-latin; mso-hansi-font-family: Aptos; mso-hansi-theme-font: minor-latin; mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&lt;span style=&quot;mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&amp;agrave;&lt;/span&gt;&lt;/span&gt; Optimizely Graph &amp;amp; Optimizely CMS &lt;span style=&quot;font-family: Wingdings; mso-ascii-font-family: Aptos; mso-ascii-theme-font: minor-latin; mso-hansi-font-family: Aptos; mso-hansi-theme-font: minor-latin; mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&lt;span style=&quot;mso-char-type: symbol; mso-symbol-font-family: Wingdings;&quot;&gt;&amp;agrave;&lt;/span&gt;&lt;/span&gt; Frontend Applications&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;When implemented correctly, it enables organizations to maintain Bynder as the single source of truth for digital assets while still providing content authors with a seamless CMS authoring experience.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot;&gt;For organizations already invested in Bynder and Optimizely SaaS CMS, the connector can significantly simplify asset governance, reduce duplication, and improve overall content delivery architecture.&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;text-align: left;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h4 class=&quot;_1l4j4na1 _1qskm0h c8urk _1a0p899 _5hqrjx _667h42&quot; style=&quot;text-align: left;&quot;&gt;&#128218; Bynder + Optimizely SaaS CMS Integration Series&lt;/h4&gt;
&lt;div class=&quot;_1l4j4na3 _667h42&quot; style=&quot;text-align: left;&quot;&gt;&lt;em class=&quot;far4bl _667h42&quot;&gt;This article is the first installment of a 3-part comprehensive architecture guide:&lt;/em&gt;&lt;/div&gt;
&lt;ul class=&quot;_667h42 _1l4j4na6&quot;&gt;
&lt;li class=&quot;_667h42 _1l4j4na2&quot; style=&quot;text-align: left;&quot;&gt;&lt;strong class=&quot;far4bl _1a0p899 _667h42&quot;&gt;&#128073; Part 1: Implementing the Connector &amp;mdash; Lessons Learned&lt;/strong&gt;&amp;nbsp;&lt;em class=&quot;far4bl _667h42&quot;&gt;(You are here)&lt;/em&gt;&lt;br /&gt;&lt;em class=&quot;far4bl _667h42&quot;&gt;What metadata syncs, how URLs behave, and how to query/render assets in a Next.js frontend.&lt;br /&gt;&lt;br /&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li class=&quot;_667h42 _1l4j4na2&quot; style=&quot;text-align: left;&quot;&gt;&lt;strong class=&quot;far4bl _1a0p899 _667h42&quot;&gt;⏭️ Part 2:&amp;nbsp;&lt;a class=&quot;_667h42 _1l4j4nac _1c9dtv3 _1by2b93 _1140xq1 _667h42&quot; href=&quot;/link/30275ac47c4843148eeda8b7d6ed282a.aspx&quot;&gt;Planning &amp;amp; Filtering &amp;mdash; Avoiding Asset Sprawl and Unnecessary Sync&lt;/a&gt;&lt;/strong&gt;&lt;br /&gt;&lt;em class=&quot;far4bl _667h42&quot;&gt;Discover how we achieved metadata-based sync filtering with zero custom code.&lt;br /&gt;&lt;br /&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li class=&quot;_667h42 _1l4j4na2&quot; style=&quot;text-align: left;&quot;&gt;&lt;strong class=&quot;far4bl _1a0p899 _667h42&quot;&gt;⏭️ Part 3:&amp;nbsp;&lt;a class=&quot;_667h42 _1l4j4nac _1c9dtv3 _1by2b93 _1140xq1 _667h42&quot; href=&quot;/link/650c1d72d9a84c28b2355e8942fc7cda.aspx&quot;&gt;Exploring Asset Lifecycle Management &amp;amp; Three-Gate Governance&lt;/a&gt;&lt;/strong&gt;&lt;br /&gt;&lt;em class=&quot;far4bl _667h42&quot;&gt;Explore a webhook-driven, three-gate model to handle asset updates, environments, and usage verification.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;</id><updated>2026-07-03T12:46:54.0000000Z</updated><summary type="html">Blog post</summary></entry></feed>