Build a Borrower Portal

Introduction

LoanPro's Loan Management System offers a customizable website where you can provide a portal for your customers. However, if you'd like to build your own customer-facing website, you'll need to incorporate LoanPro's API to send and receive information. In this article, we'll cover how to efficiently pull the most commonly used information needed for your customer portal.

If you are new to the LoanPro API, we recommend starting with our LoanPro API Introduction section of our documentation. Here, you'll learn how to connect to the API and format your headers for requests. Our documentation site linked above is also a great source for all of our available requests.

Customer Info

We imagine you'll want to include a way for customers to see their own information in your customer portal. If this is the case, you will have a few decisions to make regarding what kind of information your customers have access to within the portal. Regardless of what you decide is best for your company, we'll explain how to pull all available customer information from LoanPro.

Customer information is stored within the Customers object in LoanPro. This object can be called upon directly via the Customers endpoint, but it is also nested within the Loans entity. Here is an example of pulling information for a customer whose ID is 751. (And don't worry—this customer isn't real.)

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

Full Response

{
            "d": {
                "__metadata": {
                    "uri": "http://loanpro.simnang.com/api/public/api/1/odata.svc/Customers(id=751)",
                    "type": "Entity.Customer"
                },
                "PrimaryAddress": {
                    "__deferred": {
                        "uri": "Customers(id=751)/PrimaryAddress"
                    }
                },
                "MailAddress": {
                    "__deferred": {
                        "uri": "Customers(id=751)/MailAddress"
                    }
                },
                "Employer": {
                    "__deferred": {
                        "uri": "Customers(id=751)/Employer"
                    }
                },
                "References": {
                    "__deferred": {
                        "uri": "Customers(id=751)/References"
                    }
                },
                "PaymentAccounts": {
                    "__deferred": {
                        "uri": "Customers(id=751)/PaymentAccounts"
                    }
                },
                "Phones": {
                    "__deferred": {
                        "uri": "Customers(id=751)/Phones"
                    }
                },
                "CustomFieldValues": {
                    "__deferred": {
                        "uri": "Customers(id=751)/CustomFieldValues"
                    }
                },
                "Documents": {
                    "__deferred": {
                        "uri": "Customers(id=751)/Documents"
                    }
                },
                "CreditScore": {
                    "__deferred": {
                        "uri": "Customers(id=751)/CreditScore"
                    }
                },
                "Loans": {
                    "__deferred": {
                        "uri": "Customers(id=751)/Loans"
                    }
                },
                "LineOfCredits": {
                    "__deferred": {
                        "uri": "Customers(id=751)/LineOfCredits"
                    }
                },
                "SocialProfiles": {
                    "__deferred": {
                        "uri": "Customers(id=751)/SocialProfiles"
                    }
                },
                "Notes": {
                    "__deferred": {
                        "uri": "Customers(id=751)/Notes"
                    }
                },
                "id": 751,
                "customId": null,
                "mcId": 161057,
                "customerType": "customer.type.individual",
                "status": "Active",
                "firstName": "Douglas",
                "lastName": "McCallagher",
                "middleName": null,
                "birthDate": "/Date(-223948800)/",
                "gender": "customer.gender.male",
                "generationCode": "customer.generationCode.none",
                "email": "[email protected]",
                "ssn": "XXXXX3333",
                "driverLicense": "123456789",
                "companyName": null,
                "contactName": null,
                "customerIdType": "customer.idType.ssn",
                "customerId": null,
                "creditLimit": 0,
                "accessUserName": "dougmccallagher",
                "active": 1,
                "ofacMatch": 0,
                "ofacTested": 0,
                "saleTransferPii": 1,
                "passwordChanged": 0,
                "hasAvatar": 1,
                "loanRole": null,
                "created": "/Date(1650054921)/",
                "lastUpdate": "/Date(1650311603)/",
                "creditScoreId": 333
            }
        }
 
 

You'll notice from the response above that the Customers object has a number of nested entities such as PrimaryAddress, Employer, Phones, and more. Using an $expand token, you can pull information from multiple entities with one request, and we recommend doing so for efficiency. For example, if you need to pull a customer's address, phone, and loan information for your portal, you would send a request to the following URL:

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

Loan Info

Customer portals also commonly needed to provide the information for a customer's loans. However, due to the robustness of LoanPro's Loan Management System (LMS), loan information is a bit of a broad category. The request you will need will depend on the information you're looking for.

If you're looking for currently calculated loan breakdown information, you will need to send a request to the LoanStatusArchive endpoint. Here's the endpoint and response for a request sent for a loan with the ID of 9118:

GET https://loanpro.simnang.com/api/public/api/1/odata.svc/LoanStatusArchive(9118)

Full Response

{
            "d": {
                "__metadata": {
                    "uri": "http://loanpro.simnang.com/api/public/api/1/odata.svc/LoanStatusArchive(id=9118)",
                    "type": "Entity.StatusArchive"
                },
                "id": 9118,
                "loanId": 9118,
                "date": "/Date(1652227200)/",
                "followUpDate": "/Date(-62169984000)/",
                "amountDue": "0.00",
                "dueInterest": "113.11",
                "duePrincipal": "4073.98",
                "dueDiscount": "9.84",
                "dueEscrow": "0.00",
                "dueEscrowBreakdown": "{\"1\":0,\"2\":0,\"3\":0,\"4\":0,\"6\":0,\"30\":0,\"31\":0,\"32\":0,\"33\":0}",
                "dueFees": "3.00",
                "duePni": "4187.09",
                "payoffFees": "0.00",
                "delinquentBucket": 0,
                "delinquentBucketBalance": null,
                "nextPaymentDate": "/Date(1654732800)/",
                "nextPaymentAmount": "297.47",
                "lastPaymentDate": "/Date(1651795200)/",
                "lastPaymentAmount": "100.00",
                "principalBalance": "8902.04",
                "amountPastDue30": "0.00",
                "daysPastDue": 0,
                "dateLastCurrent": "/Date(1652227200)/",
                "dateLastCurrent30": null,
                "payoff": "9018.44",
                "perdiem": "1.10",
                "interestAccruedToday": "1.10",
                "availableCredit": "0.00",
                "creditLimit": "0.00",
                "periodStart": "/Date(1652054400)/",
                "periodEnd": "/Date(1654646400)/",
                "periodsRemaining": 16,
                "escrowBalance": "0.00",
                "escrowBalanceBreakdown": "   {\"1\":0,\"2\":0,\"3\":0,\"4\":0,\"6\":0,\"30\":0,\"31\":0,\"32\":0,\"33\":0}",
                "discountRemaining": "167.37",
                "loanStatusId": 2,
                "loanStatusText": "Open",
                "loanSubStatusId": 9,
                "loanSubStatusText": "Open - Repaying",
                "sourceCompanyId": null,
                "sourceCompanyText": null,
                "creditStatus": "loan.creditstatus.11",
                "loanAge": 609,
                "loanRecency": 5,
                "lastHumanActivity": "/Date(1651795200)/",
                "stoplight": "stoplight.display.yellow",
                "finalPaymentDate": "/Date(1694217600)/",
                "finalPaymentAmount": "4974.78",
                "netChargeOff": "0.00",
                "firstDelinquencyDate": null,
                "uniqueDelinquencies": 1,
                "delinquencyPercent": "68.03",
                "delinquentDays": 415,
                "calcedECOA": "loan.ecoacodes.1",
                "calcedECOACoBuyer": "loan.ecoacodes.0",
                "customFieldsBreakdown": "{\"15\":\"250.00\"}",
                "portfolioBreakdown": "[\"20\"]",
                "subPortfolioBreakdown": "[\"10\"]"
            }
        }
 
 

For more generic information about the loan, such as the setup terms, you can use the Loans endpoint and expand the LoanSetup object. Here's an example of what that looks like and what the response will look like:

GET https://loanpro.simnang.com/api/public/api/1/odata.svc/Loans(9118)?$expand=LoanSetup
GET https://loanpro.simnang.com/api/public/api/1/odata.svc/Loans(9118)/LoanSetup

There are two example URLs listed above, and both of them will provide the loan's setup information.

The first uses an expand token. Like shown above in the loan information example, this will result in a response that includes every nested object within the Loans object, but only the LoanSetup object will be expanded.

However, if you're planning on expanding only one object, you can send a request directly to the object like shown in the second example. This may come in handy if you only want to receive LoanSetup information in your response.

Response

...
        "LoanSetup": {
                    "__metadata": {
                        "uri": "http://loanpro.simnang.com/api/public/api/1/odata.svc/LoanSetup(id=9104)",
                        "type": "Entity.LoanSetup"
                    },
                    "id": 9104,
                    "loanId": 9118,
                    "modId": 0,
                    "active": 1,
                    "apr": "4.4966",
                    "aprForceSingle": 0,
                    "payment": "297.47",
                    "origFinalPaymentDate": "/Date(1694217600)/",
                    "origFinalPaymentAmount": "296.86",
                    "tilFinanceCharge": "708.31",
                    "tilTotalOfPayments": "10708.31",
                    "tilLoanAmount": "10000.00",
                    "tilSalesPrice": "10708.31",
                    "tilPaymentSchedule": "[{\"count\":35,\"payment\":297.47000000000003,\"startDate\":\"10\/09\/2020\"},{\"count\":1,\"payment\":296.86000000000001,\"startDate\":\"09\/09\/2023\"}]",
                    "regzCustomEnabled": 0,
                    "regzApr": "0.0000",
                    "regzFinanceCharge": "0.00",
                    "regzAmountFinanced": "0.00",
                    "regzTotalOfPayments": "0.00",
                    "loanAmount": "10000.00",
                    "discount": "500.00",
                    "underwriting": "0.00",
                    "loanRate": "4.5000",
                    "loanRateType": "loan.rateType.annually",
                    "loanTerm": "36.0000",
                    "moneyFactor": "0",
                    "residual": "0.00",
                    "contractDate": "/Date(1599609600)/",
                    "firstPaymentDate": "/Date(1602201600)/",
                    "scheduleRound": "0.00",
                    "amountDown": "0.00",
                    "reserve": "0.00",
                    "salesPrice": "0.00",
                    "gap": "0.00",
                    "warranty": "0.00",
                    "dealerProfit": "0.00",
                    "taxes": "0.00",
                    "creditLimit": "0.00",
                    "reportingCreditLimit": "0.00",
                    "loanClass": "loan.class.consumer",
                    "loanType": "loan.type.installment",
                    "discountSplit": 1,
                    "paymentFrequency": "loan.frequency.monthly",
                    "calcType": "loan.calcType.simpleInterest",
                    "daysInYear": "loan.daysInYear.actual",
                    "interestApplication": "loan.interestApplication.betweenPeriods",
                    "begEnd": "loan.begend.end",
                    "firstPeriodDays": "loan.firstPeriodDays.actual",
                    "firstDayInterest": 1,
                    "discountCalc": "loan.discountCalc.rebalancing",
                    "diyAlt": 0,
                    "dueDateOnLastDOM": 0,
                    "dueDatesOnBusinessDays": "loan.businessduedates.disabled",
                    "daysInPeriod": "loan.daysinperiod.30",
                    "roundDecimals": 5,
                    "lastAsFinal": 1,
                    "nddCalc": "loan.nddCalc.standard",
                    "endInterest": "loan.endInterest.no",
                    "scheduleTemplate": 0,
                    "curtailmentTemplate": 0,
                    "feesPaidBy": "loan.feesPaidBy.period",
                    "useInterestTiers": 0,
                    "calcHistoryEnabled": 0,
                    "calcDatesEnabled": 0,
                    "graceDays": 10,
                    "lateFeeType": "loan.lateFee.1",
                    "lateFeeAmount": "30.00",
                    "lateFeePercent": "0.00",
                    "lateFeeCalc": "loan.lateFeeCalc.standard",
                    "lateFeePercentBase": "loan.latefeepercentbase.regular",
                    "rollLastPayment": 0,
                    "paymentDateApp": "loan.pmtdateapp.actual",
                    "suspendForecastTo": null,
                    "isSetupValid": true,
                    "usuryAlert": null,
                    "maxInterestAmount": null,
                    "financeChargeAsMIA": null
                }
                ...
 
 

Payment Info

If your portal is going to display previous payment information as well, there are a few other requests that may come in handy. However, the information you need will again be dependent on what kind of access you will provide your customers within the portal.

Pulling Payment History

You can provide your customers a history of their payment history as well. There are a few different types of information that may be useful here. Nested within the Loans entity are the Autopays, Transactions, and Payments objects. Here's a breakdown of what information can be found within each object:

Object Available Information
Autopays This holds the loan's AutoPay information. Here, you will find every individual AutoPay that has been created on the account and its information.
Transactions Transactions are any action that changes the balance of a loan including payments, charges, credits, etc. Pulling transaction information will display the loan's payment schedule and how each transaction affects the payment breakdown.
Payments This holds information for individual payments made on the account. This information provided here is a snapshot of the loan when the payment is made.

We suggest taking a look at the information that each of these objects hold to determine what is needed for your portal. To pull all of this information with one request, you can use the following endpoint:

GET https://loanpro.simnang.com/api/public/api/1/odata.svc/Loans(9118)?$expand=Autopays,Transactions,Payments

Depending on the history of the loan, this response may be very long; but it will provide you with a wealth of information that could be displayed in your customer portal.

Creating New Payments

If your customer portal will be used to create payments, you'll need to follow a few steps.

Pulling Payment Profiles

First, you may desire to allow your customers to select saved payment profiles. If necessary, you can pull a customer's payment profile information using the following endpoint:

https://loanpro.simnang.com/api/public/api/1/odata.svc/Customers(751)/PaymentAccounts

The response from this request will provide saved profile information such as the profile's name, ID, status, and type. Sensitive information will not be provided.

Creating a Payment or AutoPay

To create a new payment, you'll need to use the required endpoint and payload:

PUT https://loanpro.simnang.com/api/public/api/1/odata.svc/Loans(9118)
{
    "Payments": {
        "results": [
            {
                "selectedProcessor": 0,
                "paymentMethodId": 3,
                "early": 0,
                "echeckAuthType": "payment.echeckauth.WEB",
                "amount": "303.78",
                "date": "2022-04-15",
                "info": "04/15/2022 Credit/Debit Card",
                "paymentTypeId": 1,
                "active": 1,
                "resetPastDue": 0,
                "payoffPayment": false,
                "CustomFieldValues": {
                    "results": []
                },
                "_saveProfile": 0,
                "extra": "payment.extra.tx.principal",
                "__processorName": "{\"id\":\"98\",\"key\":\"anet\",\"name\":\"Anet Default processor\",\"default\":\"1\"}",
                "paymentAccountId": 289,
                "chargeFeeType": "loan.cardfee.types.0",
                "chargeFeeAmount": 5,
                "chargeFeePercentage": 5,
                "payoffFlag": 0
            }
        ]
    }
}

There are two different ways that LoanPro's LMS categorizes payments: those that are logged only, and those that are logged and processed. The two categories differ in the information required in the payload. For more information on the distinction between the two, take a look at our Intro to Payments article.

And for a detailed explanation on how to use both payment API requests, read our Log New Payment and Process New Payment articles on our documentation site.

 

Here's the required endpoint and payload to create a new AutoPay:

PUT https://loanpro.simnang.com/api/public/api/1/odata.svc/Loans(9118)
{
    "Autopays": {
        "results": [
            {
                "name": "Default monthly autopay",
                "type": "autopay.type.recurring",
                "paymentExtraTowards": "payment.extra.tx.principal",
                "amountType": "autopay.amountType.static",
                "amount": 250.00,
                "paymentType": 1,
                "chargeServiceFee": "0",
                "processCurrent": 1,
                "retryDays": 0,
                "processTime": 22,
                "postPaymentUpdate": 1,
                "applyDate": "2016-09-18",
                "processDate": "2016-09-17",
                "methodType": "autopay.methodType.echeck",
                "recurringFrequency": "autopay.recurringFrequency.monthly",
                "recurringDateOption": "autopay.recurringDate.processDate",
                "daysInPeriod": "",
                "schedulingType": "autopay.schedulingType.calendarDay",
                "processDateCondition": "calendarDays",
                "payoffAdjustment": 1,
                "chargeOffRecovery": 0,
                "paymentMethodAuthType": "payment.echeckauth.PPD",
                "paymentMethodAccountType": "bankacct.type.checking",
                "processZeroOrNegativeBalance": 0,
                "lastDayOfMonthEnabled": 0,
                "primaryPaymentMethodId": 123,
                "recurringPeriods": 36,
                "baProcessor": "1",
                "processDateTime": "2016-09-17 22:00:00",
            }
        ]
    }
}

You can also learn more about this request in our Create New AutoPay article on our documentation site.