Adding and Updating Nested Entities

How to use objects in payloads to add and update nested entities.

Introduction

LoanPro's API uses JSON-formatted payloads. Those payloads contain objects that represent what we call ‘entities’, and some of those entities are nested within other entities. In this article, we'll explain how to use objects in your payloads to add and update entities.

How Nested Entities Work

Before we get too far, let's talk about how nested entities work. As we mentioned above, LoanPro's API uses JSON-formatted payloads. If you're familiar with LoanPro's API, you'll also know that most of our API requests that create or edit something use endpoints such as the Loans endpoint and the Customers endpoint. Both the Loans and Customers endpoints act as the parent entity for multiple, nested entities. For example, customer address information is held within a PrimaryAddress entity, and a loan's collateral information is held with the Collateral entity. Each of these nested entities—like Collateral and PrimaryAddress—are assigned their own IDs and are associated with the parent Loans or Customers entity.

Let's look at an example of customer information in action. When you send a GET request for customer information, you'll receive a long response that's full of objects that represent each nested entity:

GET https://loanpro.simnang.com/api/public/api/1/odata.svc/Customers(1183)

{  
    "d": {  
        "metadata": {  
            "uri": "http://loanpro.simnang.com/api/public/api/1/odata.svc/Customers(id=1183)",  
            "type": "Entity.Customer"  
        },  
        "PrimaryAddress": {  
            "deferred": {  
                "uri": "Customers(id=1183)/PrimaryAddress"  
            }  
        },  
        "MailAddress": {  
            "deferred": {  
                "uri": "Customers(id=1183)/MailAddress"  
            }  
        },  
        "Employer": {  
            "deferred": {  
                "uri": "Customers(id=1183)/Employer"  
            }  
        },  
        "References": {  
            "deferred": {  
                "uri": "Customers(id=1183)/References"  
            }  
        },  
        "PaymentAccounts": {  
            "deferred": {  
                "uri": "Customers(id=1183)/PaymentAccounts"  
            }  
        },  
        "Phones": {  
            "deferred": {  
                "uri": "Customers(id=1183)/Phones"  
            }  
        },  
        "CustomFieldValues": {  
            "deferred": {  
                "uri": "Customers(id=1183)/CustomFieldValues"  
            }  
        },  
        "Documents": {  
            "deferred": {  
                "uri": "Customers(id=1183)/Documents"  
            }  
        },  
        "CreditScore": {  
            "deferred": {  
                "uri": "Customers(id=1183)/CreditScore"  
            }  
        },  
        "Loans": {  
            "deferred": {  
                "uri": "Customers(id=1183)/Loans"  
            }  
        },  
        "LineOfCredits": {  
            "deferred": {  
                "uri": "Customers(id=1183)/LineOfCredits"  
            }  
        },  
        "SocialProfiles": {  
            "deferred": {  
                "uri": "Customers(id=1183)/SocialProfiles"  
            }  
        },  
        "Notes": {  
            "deferred": {  
                "uri": "Customers(id=1183)/Notes"  
            }  
        },  
        "id": 1183,  
        "customId": null,  
        "mcId": 3874462,  
        "customerType": "customer.type.individual",  
        "status": "Active",  
        "firstName": "John",  
        "lastName": "Doe",  
        "middleName": null,  
        "birthDate": "/Date(0)/",  
        "gender": "customer.gender.male",  
        "generationCode": "customer.generationCode.none",  
        "email": "[email protected]",  
        "ssn": "XXXXX1010",  
        "driverLicense": null,  
        "companyName": null,  
        "contactName": null,  
        "customerIdType": "customer.idType.ssn",  
        "customerId": null,  
        "creditLimit": 0,  
        "accessUserName": "JohnDoe212",  
        "active": 1,  
        "ofacMatch": 0,  
        "ofacTested": 0,  
        "saleTransferPii": 1,  
        "passwordChanged": 0,  
        "hasAvatar": 0,  
        "loanRole": null,  
        "created": "/Date(1632417901)/",  
        "lastUpdate": "/Date(1677534526)/",  
        "creditScoreId": 213  
    }  
}

You can also pull information that's stored within objects by including an $expand pagination filter in the URL and listing the object's name. For example, here's how you could pull a customer's PrimaryAddress information and how the response is formatted; pay attention to how the customer has an ID and the customer's address has its own, separate ID:

GET https://loanpro.simnang.com/api/public/api/1/odata.svc/Customers(1183)?$expand=PrimaryAddress

{
    "d": {
        "__metadata": {
            "uri": "http://loanpro.simnang.com/api/public/api/1/odata.svc/Customers(id=1183)",
            "type": "Entity.Customer"
        },
        "PrimaryAddress": {
            "__metadata": {
                "uri": "http://loanpro.simnang.com/api/public/api/1/odata.svc/Address(id=2554)",
                "type": "Entity.Address"
            },
            "id": 2554,
            "address1": "3844 20th Street",
            "address2": null,
            "city": "San Francisco",
            "state": "geo.state.CA",
            "zipcode": "94114",
            "country": "company.country.usa",
            "geoLat": "37.758198521738",
            "geoLon": "-122.42684494282",
            "created": "/Date(1632417901)/",
            "active": 1,
            "isVerified": 0,
            "isStandardized": 0
        },
        "MailAddress": {
            "__deferred": {
                "uri": "Customers(id=1183)/MailAddress"
            }
        },
        "Employer": {
            "__deferred": {
                "uri": "Customers(id=1183)/Employer"
            }
        },
        "References": {
            "__deferred": {
                "uri": "Customers(id=1183)/References"
            }
        },
        "PaymentAccounts": {
            "__deferred": {
                "uri": "Customers(id=1183)/PaymentAccounts"
            }
        },
        "Phones": {
            "__deferred": {
                "uri": "Customers(id=1183)/Phones"
            }
        },
        "CustomFieldValues": {
            "__deferred": {
                "uri": "Customers(id=1183)/CustomFieldValues"
            }
        },
        "Documents": {
            "__deferred": {
                "uri": "Customers(id=1183)/Documents"
            }
        },
        "CreditScore": {
            "__deferred": {
                "uri": "Customers(id=1183)/CreditScore"
            }
        },
        "Loans": {
            "__deferred": {
                "uri": "Customers(id=1183)/Loans"
            }
        },
        "LineOfCredits": {
            "__deferred": {
                "uri": "Customers(id=1183)/LineOfCredits"
            }
        },
        "SocialProfiles": {
            "__deferred": {
                "uri": "Customers(id=1183)/SocialProfiles"
            }
        },
        "Notes": {
            "__deferred": {
                "uri": "Customers(id=1183)/Notes"
            }
        },
        "id": 1183,
        "customId": null,
        "mcId": 3874462,
        "customerType": "customer.type.individual",
        "status": "Active",
        "firstName": "John",
        "lastName": "Doe",
        "middleName": null,
        "birthDate": "/Date(0)/",
        "gender": "customer.gender.male",
        "generationCode": "customer.generationCode.none",
        "email": "[email protected]",
        "ssn": "XXXXX1010",
        "driverLicense": null,
        "companyName": null,
        "contactName": null,
        "customerIdType": "customer.idType.ssn",
        "customerId": null,
        "creditLimit": 0,
        "accessUserName": "JohnDoe212",
        "active": 1,
        "ofacMatch": 0,
        "ofacTested": 0,
        "saleTransferPii": 1,
        "passwordChanged": 0,
        "hasAvatar": 0,
        "loanRole": null,
        "created": "/Date(1632417901)/",
        "lastUpdate": "/Date(1677534526)/",
        "creditScoreId": 213
    }
}

Adding Information Stored in Nested Entities

When adding information stored in a nested entity for the first time, the entity's object is added to the payload of the request. This is more straightforward than it sounds. For example, let's say we've created a customer (who is represented by an ID of 1183) but we haven't added phone information quite yet. This customer exists already but a phone number for the customer does not.

To add a phone number to the customer, we send a PUT request to the Customers endpoint and add the Phones object to our payload. Here's an example:

PUT https://loanpro.simnang.com/api/public/api/1/odata.svc/Customers(1183)

{
    "Phones": {
        "results": [
            {
                "phone": "5551234567",
                "isPrimary": "1",
                "isSecondary": "0",
                "type": "customer.phoneType.cell",
                "isLandLine": 0
            }
        ]
    }
}

Sending the request above successfully will add the phone to the customer's profile and provide an ID for the new phone number.

Updating Information Stored in Nested Entities

Updating information that's stored within nested entities requires a few things: the new information, an “__update” field, and the ID of the nested entity.

As an example, let's edit the phone number we added above. This time around, we're going to change the number itself and set it as an office phone number instead of a cell phone. To achieve this, we'll need to add that new information to the payload of our request. Additionally, we'll also need to add the "__update": true field to notify the system we're updating a nested entity, and we'll need to add an "__id" field to tell the system which phone number we're updating.

Here's the request in full:

PUT https://loanpro.simnang.com/api/public/api/1/odata.svc/Customers(1183)

{
    "Phones": {
        "results": [
            {
                "phone": "5551230000",
                "isPrimary": "0",
                "isSecondary": "1",
                "type": "customer.phoneType.office",
                "isLandLine": 0,
                "__update": true,
                "__id": 797
            }
        ]
    }
}

A request formatted like above will update the customer's phone number information. Without the "__update": true and "__id": 797 fields, the system would create a new phone number.

Common Questions

1. Do I also need to add the "__update" and "__id" fields to the payload outside of the nested object?

For example, like this:

PUT https://loanpro.simnang.com/api/public/api/1/odata.svc/Customers(1183)

{
    "Phones": {
        "results": [
            {
                "phone": "5551230000",
                "isPrimary": "0",
                "isSecondary": "1",
                "type": "customer.phoneType.office",
                "isLandLine": 0,
                "__update": true,
                "__id": 797
            }
        ]
    },
    "__update": true,
    "__id": 1183
}

You can, but it's not required and won't actually make a difference.

Some users add this information to tell the system to update the parent entity—such as a customer. But the method and endpoint of the request makes this additional payload information redundant: there's no need for the "__update" field since the system knows a PUT request is updating something, and the system knows we're updating customer 1183 because that's listed within the endpoint.

In short, you don't need to add this information when updating a nested entity. But you can if it brings you peace of mind.


2. Can I update parent entity information and nested entity information in the same request?

You can, and here's an example of that in action:

PUT https://loanpro.simnang.com/api/public/api/1/odata.svc/Customers(1183)

{
    "firstName": "Jonathan",
    "birthDate": "1970-07-17",
    "Phones": {
        "results": [
            {
                "phone": "5551230000",
                "isPrimary": "0",
                "isSecondary": "1",
                "type": "customer.phoneType.cell",
                "isLandLine": 0,
                "__update": true,
                "__id": 797
            }
        ]
    },
    "References": {
        "results": [
            {
                "name": "Tony Doe",
                "relation": "customerReference.relation.brother",
                "Address": {
                    "country": "company.country.usa",
                    "address1": "2608 Sheridan Road",
                    "zipcode": "60201",
                    "city": "Evanston",
                    "state": "geo.state.IL",
                    "__update": true,
                    "__id": 3506
                },
                "primaryPhone": "2024456655",
                "__update":true,
                "__id": 245
            }
        ]
    }
}

This payload updated the following information:

  • The customer's first name and birthdate (ID= 1183)
  • The customer's phone number (ID = 797)
  • The customer's reference's (ID=245) name, relation, phone number, address (ID= 3506)

Since the customer is the parent entity of this request (ID = 1183), we didn't add the "__update" and "__id" fields to update firstName and birthDate information. Though, those fields are required when updating the nested entities—like phone information, reference information, and (as an example of a nested entity within a nested entity) reference address information.