Dev GuideAPI Reference
Dev GuideAPI ReferenceUser GuideGitHubNuGetDev CommunitySubmit a ticketLog In
GitHubNuGetDev CommunitySubmit a ticket

Work with previews

While OCC facilitates the separation of content from its rendering, certain scenarios necessitate simultaneous viewing of the rendered content with creating content. The preview of OCC is entirely API-driven, as shown in the following flowchart.
Preview flow

The protocol has the following steps:

  1. Preview request webhook delivery or consumption.
    Preview generators should check the content type requesting to preview to be certain that it is one it can handle. When acknowledging a preview request, you need a content_hash in the preview payload at the JSON path:
    Content_hash is a digest signature of the content's version. The acknowledgment with this signature will let CMP determine whether the preview is outdated.
  2. Acknowledge preview being generated.
    Acknowledgment is necessary because CMP lacks knowledge of whether any system can generate the preview when a content preview is requested. There may be content types that never generate a preview. Therefore, when CMP identifies a system capable of generating the preview, the acknowledgment confirms its availability. Optimizely anticipates that only one system will acknowledge a single piece of content. Consequently, after receiving the initial acknowledgment, any subsequent acknowledgments will result in errors from the CMP API.
  3. Complete preview generation by submitting the links to the preview.
    The completed endpoint expects a dictionary in keyed_previews. The generator can provide multiple previews. The key can be anything, but the key is what shows in the drop-down list of previews. Some interesting capabilities with the key could be multiple languages, personalized versions of the content, rendering from across multiple channels, or any combination of these. A single preview request can aggregate different previews of the content in consideration.
    The URL needs to point to an HTML file. If the URL is behind authentication, you should use JSON web token (JWT)-based authentication to make the token part of the URL provided in completion. CMP caches the URL for perpetuity, so CMP will not access the URL beyond the first access.

Render preview with push strategies

The following push strategies render a preview for a CMS (single preview):

  • Acknowledge the preview request after checking that you can handle the content type in the payload.
  • Take the preview requested payload and create draft content for the CMS.
  • Copy the assets used in the content and refer them internally if needed. The CMP DAM's CDN (for original versions and renditions) can also serve them.
  • Generate a preview URL for the draft content created, and append auth tokens.
  • Make the completed request.
  • Wait 15 minutes, and then consider cleaning the draft content because the preview is cached on CMP.

Render preview with pull strategies

In a pull implementation, the renderer fetches data at runtime, so integration middleware does not need to create draft content per se. Rather, given the content payload, you must predictably generate the preview URL, wait for the content to be available as a draft in Optimizely Graph, and then make the completed API call with the URL.



Use HMAC authentication for retrieving draft contents from Optimizely Graph.

Also, if you want to query details about the assets related to the OCC in pull manner, use Optimizely Graph integration for publicly accessible assets. Contact your Customer Support Manager (CSM) or Application Engineer (AE) to enable the integration.

The following TypeScript code shows how to make an Optimizely Graph API call with HMAC authentication:

const contentGraphEndpoint = "";
const hmac: HmacCredentials = {
  key: "<hmac_key>",
  secret: "<hmac_secret>",

function getContentGraphHmacAuthHeader(
  method: string,
  uri: string,
  body: string
): string {
  const bodyBase64 = CryptoJS.MD5(body).toString(CryptoJS.enc.Base64);
  const key = hmac.key;
  const secret = CryptoJS.enc.Base64.parse(hmac.secret);
  const timestamp = new Date().getTime();
  const nonce = Math.random().toString(36).substring(7);
  const hmacSha = CryptoJS.HmacSHA256(
    [key, method, uri, timestamp, nonce, bodyBase64].join(""),
  const base64hmac = CryptoJS.enc.Base64.stringify(hmacSha);
  return `epi-hmac ${key}:${timestamp}:${nonce}:${base64hmac}`;

async function query(query: string) {
  const method = "POST";
  const body = JSON.stringify({ query });
  const uri = "/content/v2";

  const res = await fetch(`${contentGraphEndpoint}/${uri}`, {
    headers: {
      "Content-Type": "application/json",
      Authorization: getContentGraphHmacAuthHeader(method, uri, body),

  return await res.json();