Step 2: Migrating the schemas of legacy custom objects
This article describes in detail how to migrate the schemas of your legacy custom objects to the new custom objects experience. A schema defines the properties of a legacy custom object.
You must first migrate the object types of your legacy custom objects before you can migrate their schemas. See Step 1: Migrating the object types of legacy custom objects.
Topics covered in this article:
- Understanding how legacy schemas map to migrated custom objects
- Using the standard name and external id fields in your migrated objects
- Migrating schema properties to custom object fields
- Migrating integer properties
- Migrating number properties
- Migrating boolean properties
- Migrating string properties with no qualifying keyword
- Migrating string properties with a pattern keyword
- Migrating string properties with an enum keyword
- Migrating array properties
- Migrating properties with multiple types
- Next steps
This is the second article in a series on migrating legacy custom objects:
- Step 1: Migrating the object types of legacy custom objects
- Step 2: Migrating the schemas of legacy custom objects
- Step 3: Migrating the relationship types of legacy custom objects
- Step 4: Migrating the records of legacy custom objects
- Step 5: Migrating the relationship records of legacy custom objects
Disclaimer: Zendesk provides this article for informational purposes only. Zendesk doesn't provide support for the code in this article, nor does Zendesk support third-party technologies such as Python.
Understanding how legacy schemas map to migrated custom objects
In legacy custom objects, each object has a schema
property that defines all the properties of the object. The schema follows the JSON Schema spec so the data types of its properties consist of JSON data types such as integers, booleans, and arrays. The attributes of each property are defined by a set of keywords such as type
, description
, pattern
, and so on.
In the new custom object experience, objects don't have a defined schema or JSON data types. Instead, they have a list of custom object fields. The fields consist of form fields such as text boxes, check boxes, and drop-downs.
Example
Suppose your legacy custom object defines the following schema
property:
{
"key": "dog",
"schema": {
"properties": {
"breed": {
"description": "Dog breed",
"type": "string"
},
"training": {
"description": "Whether the dog received obedience training",
"type": "boolean"
}
...
}
}
}
After migrating the schema to the new custom object experience, the custom object has a new custom_object_fields
record that looks as follows:
{
"custom_object_fields": [
{
"key": "breed",
"title": "Breed",
"description": "Dog breed",
"type": "text",
...
},
{
"key": "training",
"title": "Training",
"description": "Whether the dog received obedience training",
"type": "checkbox",
...
},
...
]
}
Using the standard name and external id fields in your migrated objects
When custom objects are created in the new custom objects experience, they automatically have a Name field and an External ID field. These are known as the standard fields. All the custom objects you created from your legacy object types have these two fields by default. Example:

The standard custom object fields have the following field keys, respectively:
"standard::name"
"standard::external_id"
If the schema of one of your legacy custom objects has a name or external id property, you can skip migrating these properties and instead use the standard fields that have already been created for your migrated custom object. Later when you migrate the records of the legacy custom object, map the values of the records' "name" and "external_id" properties to the "standard::name" and "standard::external_id" fields of the new records.
If you decide to use the "standard::name" field, you can later require that the names entered in new records be unique. You can also have Zendesk name new records automatically using autonumbering. See Configuring how a custom object's records are named in Zendesk help. For simplicity, Zendesk recommends not turning on these features until after the records have been migrated.
Example
The following example skips any "name" or "external_id" property in a legacy schema to prevent creating duplicate fields in the custom record. When the associated records are migrated later, the values of their "name" and "external_id" fields will be mapped to the "standard::name"
and "standard::external_id"
fields that were created automatically in the migrated custom object.
schema = {
'properties': {
"name": {
"description": "The dog's name",
"type": "string"
},
...
}
}
for schema_property in schema['properties']:
if schema_property in ['name', 'external_id']:
continue
...
Migrating schema properties to custom object fields
Use the Create Custom Object Field endpoint to migrate each schema property of a legacy custom object to the associated custom object that you migrated to the new experience:
POST /api/v2/custom_objects/{custom_object_key}/fields
If you didn't change the key
value of the custom object when you migrated the legacy object, then you can use the key
property of the legacy object type for the endpoint's {custom_object_key}
path parameter.
The following table maps JSON data types to custom object fields. Click each JSON data type for more information on migrating your properties of that type.
JSON data type | Custom object field type |
---|---|
integer | integer |
number | decimal |
boolean | checkbox |
string with no qualifying keyword | text, textarea, or date |
string with pattern keyword | regexp |
string with enum keyword | dropdown |
array | text or multiselect |
multiple types | text |
Migrating integer properties
If the type
of a legacy property is "integer", then map the property to an "integer" custom object field.
Example mapping
Legacy schema property
"litter_size": {
"type": "integer",
"description": "Number of puppies in the litter"
}
Custom object field
"custom_object_field": {
"key": "litter_size",
"title": "Litter size",
"description": "Number of puppies in the litter",
"type": "integer"
}
Example script
The following Python example takes an integer property from the schema of a legacy custom object and converts it into an integer field for a migrated custom object.
schema = {
'properties': {
"litter_size": {
"type": "integer",
"description": "Number of puppies in the litter"
}
}
}
for schema_property in schema['properties']:
custom_object_field = {
'key': schema_property,
'title': schema_property.replace('_', ' ').capitalize()
}
property_keywords = schema['properties'][schema_property]
# check the property data type and map it to a field type
if property_keywords['type'] == 'integer':
custom_object_field['type'] = 'integer'
if 'description' in property_keywords:
custom_object_field['description'] = property_keywords['description']
print(custom_object_field)
Migrating number properties
If the type
of a legacy property is "number", then map the property to a "decimal" custom object field.
Example field mapping
Legacy schema property
"rating": {
"type": "number",
"description": "The average rating of the dog walker out of 5"
}
Custom object field
"custom_object_field": {
"key": "rating",
"title": "Rating",
"description": "The average rating of the dog walker on a scale of 5",
"type": "decimal"
}
Example script
The following Python example takes a number property from the schema of a legacy custom object and converts it into a decimal field for a migrated custom object.
schema = {
'properties': {
"rating": {
"type": "number",
"description": "The average rating of the dog walker out of 5"
}
}
}
for schema_property in schema['properties']:
custom_object_field = {
'key': schema_property,
'title': schema_property.replace('_', ' ').capitalize()
}
property_keywords = schema['properties'][schema_property]
# check the property data type and map it to a field type
if property_keywords['type'] == 'number':
custom_object_field['type'] = 'decimal'
if 'description' in property_keywords:
custom_object_field['description'] = property_keywords['description']
print(custom_object_field)
Migrating boolean properties
If the type
of a legacy property is "boolean", then map the property to a "checkbox" custom object field.
When migrating the records later, set the value of the object's field to true (checked) or false (unchecked):
"custom_object_record": {
"custom_object_key": "dog",
"custom_object_fields": {
"training": true,
...
},
...
},
Example mapping
Legacy schema property
"training": {
"description": "Whether the dog received obedience training",
"type": "boolean"
}
Custom object field
"custom_object_field": {
"key": "training",
"title": "Training",
"description": "Whether the dog received obedience training",
"type": "checkbox"
}
Example script
The following Python example takes a boolean property from the schema of a legacy custom object and converts it into a checkbox field for a migrated custom object.
schema = {
'properties': {
"training": {
"description": "Whether the dog received obedience training",
"type": "boolean"
}
}
}
for schema_property in schema['properties']:
custom_object_field = {
'key': schema_property,
'title': schema_property.replace('_', ' ').capitalize()
}
property_keywords = schema['properties'][schema_property]
# map the property data type to a field type
if property_keywords['type'] == 'boolean':
custom_object_field['type'] = 'checkbox'
if 'description' in property_keywords:
custom_object_field['description'] = property_keywords['description']
print(custom_object_field)
Migrating string properties with no qualifying keyword
If the type
of a legacy property is "string" and the property has no qualifying keyword such as pattern
or enum
, then map the property to a "text" custom object field. Depending on the property, you can also map it to a "textarea" or "date" field.
Example mapping
Legacy schema property
"breed": {
"type": "string",
"description": "Dog breed"
}
Custom object field
"custom_object_field": {
"key": "breed",
"title": "Breed",
"description": "Dog breed",
"type": "text"
}
Example script
The following Python example takes a string property from the schema of a legacy custom object and converts it into a text field for a migrated custom object.
schema = {
'properties': {
"breed": {
"type": "string",
"description": "Dog breed"
}
}
}
for schema_property in schema['properties']:
custom_object_field = {
'key': schema_property,
'title': schema_property.replace('_', ' ').capitalize()
}
property_keywords = schema['properties'][schema_property]
# map the property data type to a field type
if (property_keywords['type'] == 'string'
and 'enum' not in property_keywords
and 'pattern' not in property_keywords):
custom_object_field['type'] = 'text'
if 'description' in property_keywords:
custom_object_field['description'] = property_keywords['description']
print(custom_object_field)
Migrating string properties with a pattern keyword
If the type
of a legacy property is "string" and the property includes a pattern
keyword, then map the property to a regular expression ("regexp") custom object field with the regexp_for_validation
attribute.
Example mapping
Legacy schema property
"phone": {
"type": "string",
"pattern": "^(\\([0-9]{3}\\))?[0-9]{3}-[0-9]{4}$"
},
Custom object field
"custom_object_field": {
"key": "phone",
"title": "Phone number",
"type": "regexp",
"regexp_for_validation": "^(\\([0-9]{3}\\))?[0-9]{3}-[0-9]{4}$"
}
Example script
The following Python example takes a string property that has a pattern
attribute and converts it into a regexp field for a migrated custom object.
schema = {
'properties': {
"phone": {
"type": "string",
"pattern": "^(\\([0-9]{3}\\))?[0-9]{3}-[0-9]{4}$"
}
}
}
for schema_property in schema['properties']:
custom_object_field = {
'key': schema_property,
'title': schema_property.replace('_', ' ').capitalize()
}
property_keywords = schema['properties'][schema_property]
# map the property data type to a field type
if property_keywords['type'] == 'string' and 'pattern' in property_keywords:
custom_object_field['type'] = 'regexp'
custom_object_field['regexp_for_validation'] = property_keywords['pattern']
if 'description' in property_keywords:
custom_object_field['description'] = property_keywords['description']
print(custom_object_field)
Migrating string properties with an enum keyword
If the type
of a legacy property is "string" and the property has an enum
keyword, then map the property to a "drop-down" custom object field with the custom_field_options
attribute.
Example mapping
Legacy schema property
"temperament": {
"type": "string",
"description": "The dog's natural temperament",
"enum": [
"chill",
"confident",
"energetic",
"aggressive",
"couch potato"
]
},
Custom object field
"custom_object_field": {
"key": "temperament",
"title": "Temperament",
"description": "The dog's natural temperament",
"type": "dropdown",
"custom_field_options": [
"chill",
"confident",
"energetic",
"aggressive",
"couch potato"
]
}
Example script
The following Python example takes a string property that has an enum
attribute and converts it into a drop-down field for a migrated custom object.
schema = {
'properties': {
"temperament": {
"type": "string",
"description": "The dog's natural temperament",
"enum": [
"chill",
"confident",
"energetic",
"aggressive",
"couch potato"
]
}
}
}
for schema_property in schema['properties']:
custom_object_field = {
'key': schema_property,
'title': schema_property.replace('_', ' ').capitalize()
}
property_keywords = schema['properties'][schema_property]
# map the property data type to a field type
if property_keywords['type'] == 'string' and 'enum' in property_keywords:
custom_object_field['type'] = 'dropdown'
custom_field_options = [] # create the custom field options
for item in property_keywords['enum']:
option = {'name': item.capitalize(), 'value': item.replace(' ', '_')}
custom_field_options.append(option)
custom_object_field['custom_field_options'] = custom_field_options
if 'description' in property_keywords:
custom_object_field['description'] = property_keywords['description']
print(custom_object_field)
Migrating array properties
If the type
of a legacy property is "array", then map the property to a "text" field.
Then, when importing the records, store the arrays as JSON or CSV strings.
Serializing arrays as a JSON strings preserves the full array structure and allows you to parse them programmatically when you retrieve the data. Example:
"custom_object_fields": {
"walk_times": "[\"08:00\", \"12:30\", \"17:00\"]",
...
}
Mapping arrays to a comma-separated string instead of a JSON string makes the data slightly easier to read but slightly harder to parse programmatically. Example:
"custom_object_fields": {
"walk_times": "08:00,12:30,17:00",
...
}
Example mapping
Legacy schema property
"walk_times": {
"description": "Times when the dog's walks are scheduled during the day",
"type": "array"
}
Custom object field
"custom_object_field": {
"key": "walk_times",
"title": "Walk times",
"description": "Times when the dog's walks are scheduled during the day",
"type": "text"
}
Example script
The following Python example takes an array property from the schema of a legacy custom object and converts it into a text field for a migrated custom object.
schema = {
'properties': {
"walk_times": {
"description": "Times when the dog's walks are scheduled during the day",
"type": "array"
}
}
}
for schema_property in schema['properties']:
custom_object_field = {
'key': schema_property,
'title': schema_property.replace('_', ' ').capitalize()
}
property_keywords = schema['properties'][schema_property]
# map the property data type to a field type
if property_keywords['type'] == 'array':
custom_object_field['type'] = 'text'
if 'description' in property_keywords:
custom_object_field['description'] = property_keywords['description']
print(custom_object_field)
Migrating properties with multiple types
The JSON schema allows you to specify more than one type for a property. Example:
"plan_id": {
"type": ["string", "integer"]
}
If the type
of a legacy property specifies more than one type, then map the property to the "text" field.
Then, when importing the records, convert any non-string values to string values.
if isinstance(legacy_type, list):
data['custom_object_field']['type'] = 'text'
Example mapping
Legacy schema property
"plan_id": {
"type": ["string", "integer"]
}
Custom object field
"custom_object_field": {
"key": "plan_id",
"title": "Plan ID",
"type": "text"
}
Example script
The following Python example takes a property with more than one type from the schema of a legacy custom object and converts it into a text field for a migrated custom object.
schema = {
'properties': {
"plan_id": {
"type": ["string", "integer"]
}
}
}
for schema_property in schema['properties']:
custom_object_field = {
'key': schema_property,
'title': schema_property.replace('_', ' ').capitalize()
}
property_keywords = schema['properties'][schema_property]
# map the property data type to a field type
if isinstance(property_keywords['type'], list):
custom_object_field['type'] = 'text'
if 'description' in property_keywords:
custom_object_field['description'] = property_keywords['description']
print(custom_object_field)
Next steps
After migrating your legacy custom object types and their associated schemas, you can migrate their relationship types. See Step 3: Migrating the relationship types of legacy custom objects.