Content type versions
Every piece of content created in Optimizely Content Marketing Platform (CMP) relates to the latest version of the content type. Content type versions track and manage changes made to the structure of a content type. Each content type (like a blog post, product, or recipe) has a history of modifications, letting you see a history of changes and restore previous versions.
If the following changes occur to a content type, you can update a field definition version instead of creating another version. Changes outside of these conditions must create another content type.
- Change the name of a field.
- Change the description of a field.
- Order the field.
- Change a currently required field to an optional (non-required) field.
- Change an internationalization requirement to optional.
- Add an optional field to the content type.
You can also merge field definition versions in the following advanced cases:
- Merge string fields.
- Relax boundary validations for date, choice, and number fields.
- Relax list boundaries (lower a minimum or raise a maximum).
- Relax string length boundaries (increase max length, decrease min length; applies for rich text field also).
- Remove validation patterns.
- Allow additional types of assets for the library field.
- Allow new content types for the content type relation field.
Migrate content from an earlier version to the latest version
This section explains how to create and modify omnichannel content (also called structured content) by using the Optimizely CMP Public API.
The following are APIs for migrating a content's content type version:
-
Supervised Content Migration API – This API accepts a new version for the content with the target content type version. The content type version has to belong to the original content type.
-
Managed Content Migration API – This API facilitates bulk content migration, specifically targeting the migration of content type versions to the latest version of the respective content type. However, successful migration depends on the automatic migration capability of the new versions of the content types. The following factors contribute to managed migration:
- Add a required field with a default value.
- Add a non-required field (it does not matter whether there is a default value).
- Mark a non-required field as required, which has a default value.
- Expand the referenceable content types associated with a reference field.
- Change a field from singular to list.
- Remove a field.
When managed migration does not work, use supervised migration to migrate the content's content type version.
To make requests to the API, create an application in CMP and get a token. You can follow the authentication steps to do so. To get an API token without an OAuth 2.0 flow, click Generate Temporary Token in your application's Test App section in CMP.
When you have the token, copy and paste the POST and GET requests below. You should change the Authorization
header value with the token you generated and the "created_by"
value, which should be your user SSO ID.
Use the following APIs to create content for the library:
POST /structured-contents
to create contentGET /structured-contents/{id}
to fetch the content
Content Types
To create content types, use the POST /structured-content/content-types
.
The following example creates Books
for the first version with the fields title
, authorName
, and publishYear
.
curl --location 'https://api.cmp.optimizely.com/v3/structured-content/content-types' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer dc69e65d-eee8-47b1-806a-8939b95719ee' \
--data '{
"created_by": "6409c3ac8070ad1d1d8762b3",
"details": {
"name": "Books",
"component": false
},
"field_definitions": [
{
"core": {
"key": "title",
"name": "Title",
"field_type": "text-field",
"is_list": false,
"is_required": true,
"need_internationalization": false
},
"min_length": 1,
"validation_pattern": ""
},
{
"core": {
"key": "authorName",
"name": "Author Name",
"field_type": "text-field",
"is_list": false,
"is_required": true,
"need_internationalization": false
},
"min_length": 1,
"validation_pattern": ""
},
{
"core": {
"key": "publishYear",
"name": "Publish Year",
"field_type": "number",
"is_list": false,
"is_required": false,
"need_internationalization": false,
"min_value": 1.0,
"max_value": -1.0
},
"min_value": 1.0
}
]
}'
Response
{
"created": true,
"content_type_guid": "cc48519491874c7f868c7e0808457cc0",
"content_type_version_guid": "356b2a2089b14116828494bdf5e9ef87",
"content_type_version_hash": null
}
This call created "content_type_guid": "cc48519491874c7f868c7e0808457cc0"
and "content_type_version_guid": "356b2a2089b14116828494bdf5e9ef87"
.
You can view the content type versions list by GET /structured-content/content-types/<content_type_guid>/versions
.
Create contents
The following code creates two contents
using "content_type_guid": "cc48519491874c7f868c7e0808457cc0"
.
Content 1
curl --location 'https://api.cmp.optimizely.com/v3/structured-contents' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer dc69e65d-eee8-47b1-806a-8939b95719ee' \
--data '{
"title": "Clean Code",
"folder_id": null,
"content_body": {
"content_type_guid": "cc48519491874c7f868c7e0808457cc0",
"root_content": true,
"title": "Clean code",
"fields": {
"title": [
{
"locale": "en_US",
"field_values": [
{
"text_value": "Clean Code",
"order_index": null
}
]
}
],
"authorName": [
{
"locale": "en_US",
"field_values": [
{
"text_value": "Robert C. Martin",
"order_index": null
}
]
}
],
"publishYear": [
{
"locale": "en_US",
"field_values": [
{
"num_value": 2008,
"order_index": null
}
]
}
]
}
}
}'
Response
{
"id": "36ce1cfb137c44e58dc581aa9cc2aedb",
"title": "Clean Code",
"created_at": "2024-06-03T08:09:53Z",
"modified_at": "2024-06-03T08:09:53Z",
"folder_id": null,
"file_location": "/",
"is_archived": false,
"owner_organization_id": "6409c3ac8070ad1d1d8762b3",
"content_body": {
"content_guid": "36ce1cfb137c44e58dc581aa9cc2aedb",
"content_type": null,
"content_type_guid": "cc48519491874c7f868c7e0808457cc0",
"content_type_name": "Books",
"created_at": "2024-06-03T08:09:53",
"created_by": "630c9e390b6a9a1719ff2db4",
"expired": false,
"expiry_datetime": null,
"latest_fields_version": {
"content_hash": "7da99f668b9ce6c9a261522599abc81357fbdad34b0334b4719601e494260e2b26a395790c22f13fc3d996681e5448e6",
"content_type_version_guid": "356b2a2089b14116828494bdf5e9ef87",
"created_at": "2024-06-03T08:09:53",
"created_by": "630c9e390b6a9a1719ff2db4",
"fields": {
"authorName": [
{
"field_values": [
{
"order_index": null,
"text_value": "Robert C. Martin"
}
],
"locale": "en_US"
}
],
"publishYear": [
{
"field_values": [
{
"num_value": 2008.0,
"order_index": null
}
],
"locale": "en_US"
}
],
"title": [
{
"field_values": [
{
"order_index": null,
"text_value": "Clean Code"
}
],
"locale": "en_US"
}
]
},
"source_id": null,
"source_metadata": null,
"validation": {
"fields": null
},
"version_guid": "51ef14df417d49a3bf31372f548b6214"
},
"links": {
"definition": "https://api.welcomesoftware.com/v3/structured-content/content-types/cc48519491874c7f868c7e0808457cc0/versions/356b2a2089b14116828494bdf5e9ef87",
"self": "https://api.welcomesoftware.com/v3/structured-content/contents/36ce1cfb137c44e58dc581aa9cc2aedb"
},
"primary_locale": "en_US",
"root_content": true,
"source": null,
"source_id": null,
"source_metadata": null,
"title": "Clean code",
"updated_at": "2024-06-03T08:09:53",
"updated_by": "630c9e390b6a9a1719ff2db4"
},
"labels": [],
"links": {
"self": "https://api.cmp.optimizely.com/v3/structured-contents/36ce1cfb137c44e58dc581aa9cc2aedb"
}
}
This call created "content_type_guid": "36ce1cfb137c44e58dc581aa9cc2aedb"
and "content_type_version_guid": "356b2a2089b14116828494bdf5e9ef87"
.
Content 2
curl --location 'https://api.cmp.optimizely.com/v3/structured-contents' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer dc69e65d-eee8-47b1-806a-8939b95719ee' \
--data '{
"title": "Java Concurrency in Practice",
"folder_id": null,
"content_body": {
"content_type_guid": "cc48519491874c7f868c7e0808457cc0",
"root_content": true,
"title": "Java Concurrency in Practice",
"fields": {
"title": [
{
"locale": "en_US",
"field_values": [
{
"text_value": "Java Concurrency in Practice",
"order_index": null
}
]
}
],
"authorName": [
{
"locale": "en_US",
"field_values": [
{
"text_value": "Brian Goetz",
"order_index": null
}
]
}
],
"publishYear": [
{
"locale": "en_US",
"field_values": [
{
"num_value": 2006,
"order_index": null
}
]
}
]
}
}
}'
Response
{
"id": "9c1f4dbcf0eb4e3989e651f4b303692f",
"title": "Java Concurrency in Practice",
"created_at": "2024-06-03T08:13:01Z",
"modified_at": "2024-06-03T08:13:01Z",
"folder_id": null,
"file_location": "/",
"is_archived": false,
"owner_organization_id": "6409c3ac8070ad1d1d8762b3",
"content_body": {
"content_guid": "9c1f4dbcf0eb4e3989e651f4b303692f",
"content_type": null,
"content_type_guid": "cc48519491874c7f868c7e0808457cc0",
"content_type_name": "Books",
"created_at": "2024-06-03T08:13:01",
"created_by": "630c9e390b6a9a1719ff2db4",
"expired": false,
"expiry_datetime": null,
"latest_fields_version": {
"content_hash": "876aa9b14f41fc17c8b737f00f5cdbb28927c1026518ce3c545dee6f9fa1b46888ef86537d10f482b7873477e857d49c",
"content_type_version_guid": "356b2a2089b14116828494bdf5e9ef87",
"created_at": "2024-06-03T08:13:01",
"created_by": "630c9e390b6a9a1719ff2db4",
"fields": {
"authorName": [
{
"field_values": [
{
"order_index": null,
"text_value": "Brian Goetz"
}
],
"locale": "en_US"
}
],
"publishYear": [
{
"field_values": [
{
"num_value": 2006.0,
"order_index": null
}
],
"locale": "en_US"
}
],
"title": [
{
"field_values": [
{
"order_index": null,
"text_value": "Java Concurrency in Practice"
}
],
"locale": "en_US"
}
]
},
"source_id": null,
"source_metadata": null,
"validation": {
"fields": null
},
"version_guid": "698f01d972e14e15b73d6aa6d7e40f41"
},
"links": {
"definition": "https://api.welcomesoftware.com/v3/structured-content/content-types/cc48519491874c7f868c7e0808457cc0/versions/356b2a2089b14116828494bdf5e9ef87",
"self": "https://api.welcomesoftware.com/v3/structured-content/contents/9c1f4dbcf0eb4e3989e651f4b303692f"
},
"primary_locale": "en_US",
"root_content": true,
"source": null,
"source_id": null,
"source_metadata": null,
"title": "Java Concurrency in Practice",
"updated_at": "2024-06-03T08:13:01",
"updated_by": "630c9e390b6a9a1719ff2db4"
},
"labels": [],
"links": {
"self": "https://api.cmp.optimizely.com/v3/structured-contents/9c1f4dbcf0eb4e3989e651f4b303692f"
}
}
This call created "content_type_guid": "9c1f4dbcf0eb4e3989e651f4b303692f"
and "content_type_version_guid": "356b2a2089b14116828494bdf5e9ef87"
.
You can check content details by content_guid
using the following sample:
Request
curl --location 'https://api.cmp.optimizely.com/v3/structured-content/contents/9c1f4dbcf0eb4e3989e651f4b303692f?expanded=false' \
--header 'Authorization: Bearer dc69e65d-eee8-47b1-806a-8939b95719ee'
Response
{
"root_content": true,
"title": "Java Concurrency in Practice",
"primary_locale": "en_US",
"expired": false,
"expiry_datetime": null,
"source": null,
"source_id": null,
"source_metadata": null,
"latest_fields_version": {
"fields": {
"publishYear": [
{
"locale": "en_US",
"field_values": [
{
"order_index": null,
"num_value": 2006.0
}
]
}
],
"title": [
{
"locale": "en_US",
"field_values": [
{
"order_index": null,
"text_value": "Java Concurrency in Practice"
}
]
}
],
"authorName": [
{
"locale": "en_US",
"field_values": [
{
"order_index": null,
"text_value": "Brian Goetz"
}
]
}
],
"language": [
{
"locale": "en_US",
"field_values": [
{
"order_index": 1,
"text_value": "Spanish"
}
]
}
]
},
"created_by": "b46f11e3b01b4d6992e910f329cac8ba",
"source_id": null,
"source_metadata": null,
"created_at": "2024-06-03T08:30:26",
"version_guid": "7f58ed31378d4db68b098fa7709d9fd2",
"content_type_version_guid": "741c5990488d4d2894af46d732486ce7",
"content_hash": "5cbdacbb9009cae38e9bc352eb1e3e07aa63017b104b98244337e92ebfcd782854a5676bf9fd76d574bc0fe67bfdbfa5",
"validation": {
"fields": null
}
},
"content_guid": "9c1f4dbcf0eb4e3989e651f4b303692f",
"created_by": "630c9e390b6a9a1719ff2db4",
"updated_by": "630c9e390b6a9a1719ff2db4",
"created_at": "2024-06-03T08:13:01",
"updated_at": "2024-06-03T08:13:01",
"content_type_guid": "cc48519491874c7f868c7e0808457cc0",
"content_type_name": "Books",
"content_type": null,
"links": {
"self": "https://api.cmp.optimizely.com/v3/structured-content/contents/9c1f4dbcf0eb4e3989e651f4b303692f",
"definition": "https://api.cmp.optimizely.com/v3/structured-content/content-types/cc48519491874c7f868c7e0808457cc0/versions/741c5990488d4d2894af46d732486ce7"
}
}
Create content types new version (v2)
You can create content type versions by POST /structured-content/content-types/<content_type_guid>/versions
.
Request
This example adds an optional text-field
named language
.
curl --location 'https://api.cmp.optimizely.com/v3/structured-content/content-types/cc48519491874c7f868c7e0808457cc0/versions' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer dc69e65d-eee8-47b1-806a-8939b95719ee' \
--data '{
"created_by": "5756d99bddf4e48c1e00003a",
"details": {
"name": "Books",
"component": false
},
"field_definitions": [
{
"core": {
"key": "title",
"name": "Title",
"field_type": "text-field",
"is_list": false,
"is_required": true,
"need_internationalization": false
},
"min_length": 1,
"validation_pattern": ""
},
{
"core": {
"key": "authorName",
"name": "Author Name",
"field_type": "text-field",
"is_list": false,
"is_required": true,
"need_internationalization": false
},
"min_length": 1,
"validation_pattern": ""
},
{
"core": {
"key": "publishYear",
"name": "Publish Year",
"field_type": "number",
"is_list": false,
"is_required": false,
"need_internationalization": false,
"min_value": 1.0,
"max_value": -1.0
},
"min_value": 1.0
},
{
"core": {
"key": "language",
"name": "Language",
"field_type": "text-field",
"is_list": false,
"is_required": false,
"need_internationalization": false
},
"min_length": 1,
"validation_pattern": ""
}
]
}'
Response
{
"created": true,
"content_type_guid": "cc48519491874c7f868c7e0808457cc0",
"content_type_version_guid": "741c5990488d4d2894af46d732486ce7",
"content_type_version_hash": "8b909f3a17778b108e5d7f4cfa58c988ae28a2c927f216c3ace78d4586ac750e4010acc73c036774afb55dc799b9a7f3"
}
This created a content type version with a nullable
text field named language
and "content_type_version_guid": "741c5990488d4d2894af46d732486ce7"
.
You can check all content types versions by GET /structured-content/content-types/<content_type_guid>/versions
.
Managed migrations
The "content_type_guid": "cc48519491874c7f868c7e0808457cc0"
has two versions:
Content Type version 1: `356b2a2089b14116828494bdf5e9ef87`
Content Type version 2: `741c5990488d4d2894af46d732486ce7` [latest]
For Content Type version 1, content_type_version_guid
has two contents. This example migrates the contents from "content_type_version_guid": "356b2a2089b14116828494bdf5e9ef87"
to "content_type_version_guid": "741c5990488d4d2894af46d732486ce7"
.
The managed migrations APIs are
- Validate managed migrations –
POST /structured-content/content-types/<content_type_guid>/managed-migrations/validate
. - Create managed migrations –
POST /structured-content/content-types/<content_type_guid>/managed-migrations
. - Retrieve all managed migrations –
GET /structured-content/content-types/<content_type_guid>/managed-migrations/<job_id>
. - Edit managed migrations –
PATCH /structured-content/content-types/<content_type_guid>/managed-migrations/<job_id>
. - Delete managed migrations –
DELETE /structured-content/content-types/<content_type_guid>/managed-migrations/<job_id>
. - Start managed migrations –
POST /structured-content/content-types/<content_type_guid>/managed-migrations/<job_id>/start
.
Validate managed migrations
You can validate the managed migrations possibility by POST /structured-content/content-types/<content_type_guid>/managed-migrations/validate
.
Request
curl --location 'https://api.cmp.optimizely.com/v3/structured-content/content-types/cc48519491874c7f868c7e0808457cc0/managed-migrations/validate' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer dc69e65d-eee8-47b1-806a-8939b95719ee' \
--data '{
"source_content_type_version_id": "356b2a2089b14116828494bdf5e9ef87",
"default_values": {
"language": [
{
"locale": "en_US",
"fieldValues": [
{
"orderIndex": 0,
"text_value": "English"
}
]
}
]
}
}'
This sends the added field values introduced in the latest version with default_values
.
Response
{
"is_managed_migration_possible": True
}
Create managed migrations
You can create managed migrations by POST /structured-content/content-types/<content_type_guid>/managed-migrations
.
Request
curl --location 'https://api.cmp.optimizely.com/v3/structured-content/content-types/cc48519491874c7f868c7e0808457cc0/managed-migrations' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer dc69e65d-eee8-47b1-806a-8939b95719ee' \
--data '{
"source_content_type_version_id": "356b2a2089b14116828494bdf5e9ef87",
"createdBy": "b46f11e3b01b4d6992e910f329cac8ba",
"default_values": {
"language": [
{
"locale": "en_US",
"field_values": [
{
"order_index": 1,
"text_value": "English"
}
]
}
]
}
}'
Here, source_content_type_version_id
is the first content_type_version_id
, and default_values
are default fields you want to add for contents.
Response
{
"created": true,
"job_id": "c4c61196bc6a4c07a7e708a8b6d74ae2"
}
The managed migration is created with "job_id": "c4c61196bc6a4c07a7e708a8b6d74ae2"
.
Fetch managed migrations
You can retrieve managed migrations by GET /structured-content/content-types/<content_type_guid>/managed-migrations/<job_id>
.
Pass the job_id
as parameter.
curl --location 'https://api.cmp.optimizely.com/v3/structured-content/content-types/cc48519491874c7f868c7e0808457cc0/managed-migrations/c4c61196bc6a4c07a7e708a8b6d74ae2' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer dc69e65d-eee8-47b1-806a-8939b95719ee'
Response
{
"id": "c4c61196bc6a4c07a7e708a8b6d74ae2",
"instance_id": "6409c3ac8070ad1d1d8762b3",
"content_type_id": "cc48519491874c7f868c7e0808457cc0",
"source_content_type_version_id": "356b2a2089b14116828494bdf5e9ef87",
"target_content_type_version_id": "741c5990488d4d2894af46d732486ce7",
"default_values": {
"language": [
{
"locale": "en_US",
"field_values": [
{
"text_value": "English",
"order_index": 1
}
]
}
]
},
"status": "not_started",
"created_at": "2024-06-03T08:23:22",
"updated_at": "2024-06-03T08:23:22",
"content_migration_summary": {
"total": 2,
"not_started": 2,
"succeeded": 0,
"errored": 0,
"skipped": 0
}
}
Patch managed migrations
You can update managed migrations jobs by PATCH /structured-content/content-types/<content_type_guid>/managed-migrations/<job_id>
.
PATCH only supports if the job status
is not_started
, and it can only update the default_values
. This example changes the language
from English
to Spanish
.
Request
curl --location --request PATCH 'https://api.cmp.optimizely.com/v3/structured-content/content-types/cc48519491874c7f868c7e0808457cc0/managed-migrations/c4c61196bc6a4c07a7e708a8b6d74ae2' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer dc69e65d-eee8-47b1-806a-8939b95719ee' \
--data '{
"source_content_type_version_id": "356b2a2089b14116828494bdf5e9ef87",
"createdBy": "b46f11e3b01b4d6992e910f329cac8ba",
"default_values": {
"language": [
{
"locale": "en_US",
"field_values": [
{
"order_index": 1,
"text_value": "Spanish"
}
]
}
]
}
}'
Response
{
"updated": true
}
Delete managed migrations
You can delete managed migrations jobs by DELETE /structured-content/content-types/<content_type_guid>/managed-migrations/<job_id>
.
Request
curl --location --request DELETE 'https://api.cmp.optimizely.com/v3/structured-content/content-types/cc48519491874c7f868c7e0808457cc0/managed-migrations/c4c61196bc6a4c07a7e708a8b6d74ae2' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer dc69e65d-eee8-47b1-806a-8939b95719ee'
Response
The response status will be 204
.
Start managed migrations job
You can start managed migrations jobs by POST /structured-content/content-types/<content_type_guid>/managed-migrations/<job_id>/start
.
Request
curl --location 'https://api.cmp.optimizely.com/v3/structured-content/content-types/cc48519491874c7f868c7e0808457cc0/managed-migrations/c4c61196bc6a4c07a7e708a8b6d74ae2/start' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer dc69e65d-eee8-47b1-806a-8939b95719ee' \
--data '{
"started": true,
"job_id": "c4c61196bc6a4c07a7e708a8b6d74ae2"
}'
Response
{
"started": true,
"job_id": "c4c61196bc6a4c07a7e708a8b6d74ae2"
}
This starts the jobs and automatically migrates contents to new content type versions with default values.
Content type versions in Optimizely Graph
Every content type version is a separate schema in Optimizely Graph. Query against the base type in a schema when you query for a content type. The versions typically have a suffix of Vn
where n
is a positive integer of the version's sequence, such as V1
,V2
, and so on.
Leverage instances to test models
For omnichannel content (OCC), set up at least one additional instance alongside the production instance. Use the additional instance for modeling content types. Also, use the content type APIs to copy content types from one instance to another, which gives you greater control in developing, testing, and delivering content types to production.
The following reasons make managing content types as source code a powerful practice. This approach promotes consistency, reliability, control, and adaptability in managing your content structure—essential when scaling content operations.
- Version control – Provide robust version tracking for your content models (with tools like Git). You can view a detailed history of changes and easy rollback options and collaborate on adjustments.
- Automation – Define content types as code to enable seamless integration into continuous integration or continuous deployment (CI/CD) pipelines. You can test, deploy, and potentially roll back changes automatically.
- Infrastructure as code alignment – Standardize how you manage configuration across your project alongside traditional code components.
- Auditability and transparency – Log modifications to content types the same way as any code changes to improve record-keeping and accountability for content structure decisions.
- Portability – Facilitate migration between environments (dev, staging, production) with code-based content types to minimize inconsistencies and manual errors.
Updated about 2 months ago