This article describes how to migrate the relationship records of your legacy objects to the new custom objects experience. Relationship records define the relationships between existing records.

Topics covered in this article:

This is the fifth and last article in a series on migrating legacy custom objects:

Understanding how relationship records map to object records in the new custom objects experience

In the legacy custom objects experience, a relationship between two records is established by a relationship record. Example:

{  "data": {    "id": "1a034055-6485-11ea-a338-a7395da450f1",    "relationship_type": "dogs_assigned_to_walker",    "source": "zen:user:623577345673",    "target": "fc7925e3-6484-11ea-a338-19eb2b9d27d1"  }}

In the new custom objects experience, a relationship between two records is established when the value of a lookup relationship field on a record is updated. For example, to create a "dogs_assigned_to_walker" relationship between records, an agent might update an "Assigned walker" lookup field on a dog record with the id of the Zendesk user. After the update, the dog record might look as follows:

{  "custom_object_record": {    "custom_object_fields": {      "breed": "Corgi",      "training": false,      "assigned_walker": 3462224532    },    "name": "Miso",    "id": "01GDXYEY1FQYN066VHF49YHJ21"  }}

The lookup field is always on the records of the "many" object in a "many-to-one" relationship. This corresponds to the target object of the associated legacy relationship type. As a result, you always update the record specified by the target record of the legacy relationship record. This record is either from a Zendesk standard object or from a migrated custom object.

The JSON format of a Zendesk standard record is different from the format of a custom object record like the one shown in the example above. For example, suppose Zendesk user records have a lookup relationship field. The field might looks as follows in a user record:

{  "user": {    "id": 623577345673,    "custom_fields": [      {          "id": 23618791821075,          "value": "01HGK5DPHSAWGVE492CAWZSE7A"      },      ...    ]    ...  }}

Lookup fields and other custom fields are contained in the custom_fields array of Zendesk standard records. The fields are identified only by their 'id'.

You must identify and update a specific lookup relationship field in the array. The value of a lookup relationship field is a record id.

Even if the custom fields in a Zendesk standard record are contained in an array, you don't need to retrieve the full array to update a specific field. You can update a specific field in the array. The API will merge the updated field with the existing fields. The other fields won't be overwritten.

Use the endpoint to update the specific Zendesk record to update the lookup field:

Migrating the record relationships of legacy custom objects

Migrating a relationship record consists of assigning the relationship record's source record id to the lookup field on the relationship's target record.

The record ids of standard Zendesk objects are the same in both custom objects experiences. You can assign these to lookup fields as is.

However, the record ids of legacy records are different from their associated migrated records. When you migrated the records in the previous step, you created a map of record ids that associated each legacy record id to a new record id. Use this map the assign the correct record ids to the lookup fields of records.

You can retrieve relationships records by relationship type. After retrieving the relationship records, you can use them to update the lookup fields of records in the new experience.

Example main function logic

The following example main() function shows the general logic of migrating legacy relationship records to the new custom objects experience. The called functions are explained in the follow-up sections.

def main():    record_map = read_json_file('record_map.json')    lookup_field_map = read_json_file('lookup_field_map.json')
    relationship_types = get_relationship_types()    for relationship_type in relationship_types:        relationship_records = get_relationship_records(relationship_type)        for relationship_record in relationship_records:            if relationship_record['target'][:4] == 'zen:':                update_zen_object_target_record(relationship_record, record_map, lookup_field_map)            else:                update_custom_object_target_record(relationship_record, record_map, relationship_type)
# utility functiondef read_json_file(file_path):    with open(file_path, mode='r', encoding='utf-8') as f:        content = json.load(f)    return content

Example function for getting relationship records

You can retrieve relationship records by relationship type:

  • GET /api/sunshine/relationships/records?type={relationship_type}

See List Legacy Relationship Records by Type.

The following example Python functions gets the relationship records for a specific relationship type.

def get_relationship_records(relationship_type):    relationship_records = []    endpoint = f'api/sunshine/relationships/records'    url = f'https://{ZENDESK_SUBDOMAIN}.zendesk.com/{endpoint}'    params = {'type': relationship_type["key"], 'per_page': 1000}    auth = f'{ZENDESK_USER_EMAIL}/token', ZENDESK_API_TOKEN    response = requests.get(url, params=params, auth=auth)
    # paginate    while url:        if response.status_code != 200:            print(f'Failed to retrieve relationship records: {response.status_code}: {response.text}')            return []        records = response.json()['data']        if records:            relationship_records.extend(records)
        links = response.json()['links']        if links['next']:            url = f'https://{ZENDESK_SUBDOMAIN}.zendesk.com{links["next"]}'            response = requests.get(url, auth=auth)        else:            url = ''
    return relationship_records

Example function for updating Zendesk object records

The following example Python function updates a lookup field in a Zendesk object record.

def update_zen_object_target_record(rel_record, record_map, lookup_field_map):
    # replace legacy record ids    rel_record = update_legacy_record_ids(rel_record, record_map)  # see below    if not rel_record:        return {}
    # gather the request parameters    source_record_id = rel_record['source']    if source_record_id[:4] == 'zen:':        source_record_id = source_record_id.split(':')[2]    target_record_id = rel_record['target'].split(':')[2]    zen_object = rel_record['target'].split(':')[1]     # example, user in zen:user:373545462053    lookup_field_id = lookup_field_map[rel_record['relationship_type']]
    # create the request body    custom_fields = [{'id': lookup_field_id, 'value': source_record_id}]    data = {zen_object: {'custom_fields': custom_fields}}
    # make the request    endpoint = f'api/v2/{zen_object}s/{target_record_id}'    url = f'https://{ZENDESK_SUBDOMAIN}.zendesk.com/{endpoint}'    response = requests.put(url, json=data, auth=AUTH)    if response.status_code != 200:        print(f'{response.status_code}: {response.text}')        return {}    return response.json()
# utility functiondef update_legacy_record_ids(rel_record, record_map) -> dict:    for record_type in ['source', 'target']:        record_id = rel_record[record_type]        if record_id[:4] == 'zen:':            continue        if record_id not in record_map:            print(f'- Error: Record {record_id} not found in record map')            return {}        rel_record[record_type] = record_map[record_id]    return rel_record

Example function for updating custom object records

The following example Python function updates a lookup field in a custom object record.

def update_custom_object_target_record(rel_record, record_map, rel_type):
    # replace legacy record ids    rel_record = update_legacy_record_ids(rel_record, record_map)    if not rel_record:        return {}
    # gather the request parameters    target_record_id = rel_record['target']    source_record_id = rel_record['source']    if source_record_id[:4] == 'zen:':        source_record_id = source_record_id.split(':')[2]    lookup_field_name = rel_record['relationship_type']     # assumes same key for migrated lookup field
    # create the request body    data = {        'custom_object_record': {            'custom_object_fields': {                f'{lookup_field_name}': f'{source_record_id}',            }        }    }
    # make the request    target_object_key = rel_type['target'][0]   # assumes same key for migrated custom object    endpoint = f'api/v2/custom_objects/{target_object_key}/records/{target_record_id}'    url = f'https://{ZENDESK_SUBDOMAIN}.zendesk.com/{endpoint}'    response = requests.patch(url, json=data, auth=AUTH)    if response.status_code != 200:        print(f'{response.status_code}: {response.text}')        return {}    return response.json()

Next steps

This article is the last in this five-part series on migrating legacy custom objects.