Dev guideRecipesAPI ReferenceChangelog
Dev guideRecipesUser GuidesNuGetDev CommunityOptimizely AcademySubmit a ticketLog In
Dev guide

Sync content types for external data

Add custom data to Optimizely Graph and query it using the GraphQL API with custom content sources.

Optimizely Graph provides endpoints that let you define the GraphQL schema of an external content source. Defining a schema lets you query the data you upload through the same GraphQL conventions as content indexed automatically from Optimizely products, so a single query can retrieve results across all your sources.

🚧

Important

This feature is not enabled by default. Contact Optimizely to request additional content sources.

Prerequisites

Before you call the content type endpoints, confirm the following:

  • External content sources are enabled for your account. Contact Optimizely if they are not.
  • You have a content source ID of four characters or fewer, lowercase, containing only a-z and 0-9.
  • You have AppKey and Secret credentials with permissions to call the types endpoints.
  • Content type names do not conflict with names already defined in Optimizely CMS or in other external sources.

Synchronize full content definition

Push the complete schema of a content source to Optimizely Graph in a single request. Use this endpoint when you create a new content source or rewrite an existing schema.

📘

Note

The schema does not work if several content types have the same name across sources. Content types from external sources must have unique names that do not conflict with content types in Optimizely CMS.

For information, refer to the Full content type update API reference.

The full content type endpoint syncs the full definition of a content type to the service.

The following minimal example creates a content source named src2 with a single content type test that has one string property Name:

curl --location --request PUT 'https://cg.optimizely.com/api/content/v3/types?id=src2' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic BASE64_ENCODED_CREDENTIALS' \
--data '{
 "languages": ["en"],
 "contentTypes":{
   "test": {
     "contentType": [],
     "properties": {
       "Name": {
        "type": "String"
       }
     }
   }
 }
}
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Put, "https://cg.optimizely.com/api/content/v3/types?id=src2");
request.Headers.Add("Authorization", "Basic BASE64_ENCODED_CREDENTIALS");
var content = new StringContent("{\n \"languages\": [\"en\"],\n \"contentTypes\":{\n   \"test\": {\n     \"contentType\": [],\n     \"properties\": {\n       \"Name\": {\n        \"type\": \"String\"\n       }\n     }\n   }\n }\n}", null, "application/json");
request.Content = content;
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());

The example uses the following parameters:

  • id=src2 – Source name, up to four characters.
  • languages – Required and not empty.
  • contentTypes – Required and not empty.
  • test – Content type name.
  • contentType – Required.
  • properties – Required and not empty.

The preceding request creates a src2 data source and a GraphQL schema that matches the content type.

🚧

Important

Uploading a full content definition using the full content type endpoint deletes all data in the content source.

Example

The following example defines property types, languages, and content types for a content source containing product information. The example uses simple data types and the ProductLanguage property type.

{
    "propertyTypes": {
        "ProductLanguage": {
            "properties": {
                "DisplayName": {
                    "name": "DisplayName",
                    "type": "String"
                },
                "Name": {
                    "name": "Name",
                    "type": "String"
                }
            }
        }
    },
    "label": "Commerce",
    "languages": [
        "en"
    ],
    "contentTypes": {
        "Catalog": {
            "abstract": true,
            "contentType": [],
            "properties": {
                "Id": {
                    "type": "String"
                },
                "Name": {
                    "type": "String"
                }
            }
        },
        "Product": {
            "contentType": [
                "Catalog"
            ],
            "properties": {
                "Id": {
                    "type": "String"
                },
                "Name": {
                    "type": "String"
                },
                "Language":{ 
                    "type": "ProductLanguage"
                },
                "Quantity": {
                    "type": "Int"
                },
                "Size": {
                    "type": "Float"
                },
                "Color": {
                    "type": "String"
                }
            }
        }
    }
}

The following curl command issues a PUT request with the definition using the ID com, which is the name of the content source. The endpoint creates the content source if it does not already exist.

❗️

Important

If you create more content sources than your account allows, the call fails and the content source is not created.

curl --location --request PUT 'https://cg.optimizely.com/api/content/v3/types?id=com' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic BASE64_ENCODED_CREDENTIALS' \
--data '{
    "propertyTypes": {
        "ProductLanguage": {
            "properties": {
                "DisplayName": {
                    "name": "DisplayName",
                    "type": "String"
                },
                "Name": {
                    "name": "Name",
                    "type": "String"
                }
            }
        }
    },
    "label": "Commerce",
    "languages": [
        "en"
    ],
    "contentTypes": {
        "Catalog": {
            "abstract": true,
            "contentType": [],
            "properties": {
                "Id": {
                    "type": "String"
                },
                "Name": {
                    "type": "String"
                }
            }
        },
        "Product": {
            "contentType": [
                "Catalog"
            ],
            "properties": {
                "Id": {
                    "type": "String"
                },
                "Name": {
                    "type": "String"
                },
                "Language":{ 
                    "type": "ProductLanguage"
                },
                "Quantity": {
                    "type": "Int"
                },
                "Size": {
                    "type": "Float"
                },
                "Color": {
                    "type": "String"
                }
            }
        }
    }
}'

Authorization

The content type endpoints require authentication. Pick Basic for quick local testing and epi-hmac for production calls that require request signing.

The supported authorization types are Basic and epi-hmac:

Basic:

  • Username – AppKey
  • Password – Secret

Encode the combined AppKey:Secret string using base64, then set the header as Authorization: Basic base64(AppKey:Secret).

epi-hmac – The AppKey and Secret are signed using the HMAC algorithm.

var crypto = require("crypto-js");
var sdk = require('postman-collection');
// This script uses 2 variables.
//
// EPTSKey
// EPTSSecret
//
var method = pm.request.method;
var key = pm.variables.get("EPTSKey");
var secret = CryptoJS.enc.Base64.parse(pm.variables.get("EPTSSecret"));
var target = new sdk.Url(request.url).getPathWithQuery();
var timestamp = (new Date()).getTime();
var nonce = Math.random().toString(36).substring(7);
var body = "";
if( pm.request.body )
{
    body = pm.request.body.raw;
}
var bodybase64 = crypto.MD5(body).toString(CryptoJS.enc.Base64);
var hmac = crypto.HmacSHA256(key + method + target + timestamp + nonce + bodybase64, secret);
var base64hmac = CryptoJS.enc.Base64.stringify(hmac);
var header = "epi-hmac " + key + ":" + timestamp +":" + nonce + ":" + base64hmac;
pm.request.headers.add(header, "Authorization")

Synchronize partial content type

Update specific properties of a content type without resending the full schema. Use this endpoint when you add a searchable flag, change a property type, or extend an existing content type.

📘

Note

For information, refer to the Partial content type update API reference.

Call synchronize full content type before using this endpoint to update a partial content type.

The partial content type endpoint syncs a partial content type to the service.

Example

The following example makes the Name property searchable by adding "searchable": true to its definition.

{
    "languages": [
        "en"
    ],
    "contentTypes": {
        "Product": {
            "contentType": [
                "Catalog"
            ],
            "properties": {
                "Id": {
                    "type": "String"
                },
                "Name": {
                    "type": "String",
                  	"searchable": true
                },
                "Language":{ 
                    "type": "ProductLanguage"
                },
                "Quantity": {
                    "type": "Int"
                },
                "Size": {
                    "type": "Float"
                },
                "Color": {
                    "type": "String"
                }
            }
        }
    }
}

The data is sent to the types endpoint using POST instead of PUT, and the com ID specifies the target content source.

curl --location 'https://cg.optimizely.com/api/content/v3/types?id=com' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic BASE64_ENCODED_CREDENTIALS' \
--data '{
    "languages": [
        "en"
    ],
    "contentTypes": {
        "Product": {
            "contentType": [
                "Catalog"
            ],
            "properties": {
                "Id": {
                    "type": "String"
                },
                "Name": {
                    "type": "String",
                  	"searchable": true
                },
                "Language":{ 
                    "type": "ProductLanguage"
                },
                "Quantity": {
                    "type": "Int"
                },
                "Size": {
                    "type": "Float"
                },
                "Color": {
                    "type": "String"
                }
            }
        }
    }
}'