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:

This is the second article in a series on migrating 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 typeCustom object field type
integerinteger
numberdecimal
booleancheckbox
string with no qualifying keywordtext, textarea, or date
string with pattern keywordregexp
string with enum keyworddropdown
arraytext or multiselect
multiple typestext

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.