This is a staging build of the docs
We've reorganized our products to make building with Codat easier than ever
Skip to main content

Loan writeback for general lending

Simplify your implementation with our loan writeback best practices guide and correctly account for a loan programmatically

Concept overview

This guide takes you through the steps needed to implement and run the loan writeback procedure in your lending business using Codat. You will learn how to configure Codat and use it to create transactions that represent the deposit and repayment of the loan in your customers' accounting platforms.

This solution supports loan writeback procedure for general lending, such as term loans, but does not cover requirements specific to invoice finance.

What is loan writeback?

Loan writeback (also known as lending writeback) is the process of continuously updating an accounting platform with information on a loan. It helps maintain an accurate position of the loan during the entire lending cycle by recording the loan liability, any interest, fees, or repayments, and facilitating the reconciliation of bank transactions.

Mandatory loan writeback

Certain accounting platforms require lenders to continuously update their books with money lent to SMBs. For example, Xero obligates lenders going through the App Partner certification process to handle the writeback process.

Why use it?

A bookkeeper can account for a loan in numerous ways in an accounting platform. For example, some bookkeepers may erroneously register a loan as a direct income or even a sales invoice. This results in loans being improperly recorded as revenue and repayments as operating costs. At the end of the reporting period, this can make it hard for the bookkeeper to close their books.

By implementing loan writeback functionality in your application, you can make sure loan bookeeping is done regularly, correctly, and quickly, and always see an up-to-date state of the borrower's accounts.

What's the process?

The process of loan writeback involves recording loan withdrawals, repayments, and interest in the SMB's accounting platform. It can be split into three stages, as shown on the diagram below:

  1. Configure loan writeback for your SMB customer.

  2. Deposit funds into your SMB's accounting platform.

  3. Repay money owed to you, the lender, in your SMB's accounting platform.

Prerequisites

  • Check that you have created a Codat company that represents your SMB customer and linked it to an accounting platform. If you are already using Codat for lending, it's likely you have previously created some companies.

You should also create and connect a test company to use while building your solution.

  • If you are implementing loan writeback for Xero, Xero Bank Feeds API needs to be enabled for your registered app. Xero usually does this during the certification process for lenders' apps so that you can test your solution before completing the certification.

  • Provide the customer with a user interface that gives the option to enable the loan writeback process flow and configure or update their account mapping, for example:

Example mapping UI

Map your payroll components to a relevant account.

Deposit account

Select the business bank account where the funds will be deposited.

Expense account

Select or create the expense account you want any fees or interest tracked against.

Lender account

We'll also create new bank account in your accounting platform

This account acts as the lenders virtual account and is used to ensure bookkeeping is performed correctly.

Configure loan writeback

First, your SMB customer will use your UI to configure loan writeback accounts so that the accounting entries are reflected correctly in their accounting platform. They will create or select existing, and subsequently map, the following elements:

  • SMB bank account, the borrower's business account where the loan is deposited.
  • Lender bank account, a virtual account that contains the lender's transactions.
  • Expense account, an account to record incurred fees and interest.
  • Supplier record, a record to identify you, the lender, in future transactions.

Let's go through that in detail. On the diagram below, you can see the configuration sequence covering the display and selection of a bank account, an expense account, and a supplier record. Alternative steps are also provided in case a new account and a new supplier need to be created.

Bank account

Loan writeback process operates with two bank accounts:

  • A borrower's business bank account where the money lent is deposited.
  • A lender's bank account, which is a virtual bank account in the accounting platform that acts as a container for lender transactions.

First, your customer needs to choose one of their existing bank accounts. This account will be used to depost the loan. Call our List bank accounts endpoint to retrieve the customer's existing bank accounts.

codatLending.accountingBankData.accounts.list({
companyId: "8a210b68-6988-11ed-a1eb-0242ac120002",
connectionId: "2e9d2c44-f675-40ba-8049-353bfcb5e171"
}).then((res: ListAccountingBankAccountsResponse) => {
if (res.statusCode == 200) {
// handle response
}
});

Display the response to the customer and allow them to select the account. Store the returned id and use it as the borrower's accountId in future operations.

Next, create a new bank account that will act as a virtual bank account for your lending activity:

  1. Use our Get create/update bank account model to get the expected data for the bank account creation request payload. The data required can vary depending on the platform.
  2. Use the Create bank account endpoint to create the new account in the accounting platform.
codatLending.loanWriteback.bankAccounts.create({
accountingBankAccount: {
accountName: "saepe",
accountNumber: "fuga",
accountType: AccountingBankAccountType.Credit,
availableBalance: 3595.08,
balance: 6130.64,
currency: "USD",
iBan: "saepe",
institution: "commodi",
nominalCode: "molestiae",
overdraftLimit: 2444.25,
sortCode: "error",
},
companyId: "8a210b68-6988-11ed-a1eb-0242ac120002",
connectionId: "2e9d2c44-f675-40ba-8049-353bfcb5e171",
}).then((res: CreateBankAccountResponse) => {
if (res.statusCode == 200) {
// handle response
}
});

In response, you will receive account creation details which you can display to your customer. Just as you did with the accountId, store the id and use it as lenderBankAccountId in future transactions.

Supplier

In order to create a spend money transaction, Codat requires you, the lender, to be represented as a supplier in your SMB's accounting system.

Let your customer check if your record already exists in their accounts. Use our List suppliers endpoint to fetch the list of existing suppliers.

codatLending.accountsPayable.suppliers.list({
companyId: "8a210b68-6988-11ed-a1eb-0242ac120002",
}).then((res: ListAccountingSuppliersResponse) => {
if (res.statusCode == 200) {
// handle response
}
});

Display the response to the customer and allow them to find and select your lender record in their supplier list. Store the id and use it as supplier Id in future transactions.

If this is the first time you have lent to this SMB customer, you may need to create yourself as a new supplier in their accounting platform. First, use our Get create/update supplier model to get the expected data for the supplier creation request payload. Next, use that payload and call our Create supplier endpoint.

codatLending.loanWriteback.suppliers.create({
accountingSupplier: {
addresses: [
{
line1: "Stoney Business Park",
city: "London",
country: "UK",
postalCode: "SE14 1PE",
type: AccountingAddressType.Billing,
},
],
contactName: "David",
defaultCurrency: "GBP",
emailAddress: "david@example.com",
phone: "+44 25691 154789",
registrationNumber: "0115633",
status: SupplierStatus.Active,
supplierName: "Bank of Dave",
},
companyId: "8a210b68-6988-11ed-a1eb-0242ac120002",
connectionId: "2e9d2c44-f675-40ba-8049-353bfcb5e171",
}).then((res: CreateSupplierResponse) => {
if (res.statusCode == 200) {
// handle response
}
});

Similarly, store the id and use it in future transactions.

Expense account

Finally, use our List accounts endpoint filtered by type=Expense to retrieve the customer's existing expense accounts. Let them choose one that will be used to record fees and interest.

codatLending.financialStatements.accounts.list({
companyId: "8a210b68-6988-11ed-a1eb-0242ac120002",
query: "type=Expense",
}).then((res: ListAccountingAccountsResponse) => {
if (res.statusCode == 200) {
// handle response
}
});

Display the response to the customer and allow them to select the desired expense account. Store the id and use it as the expense account Id in future operations.

If the customer wants to create a new nominal expense account for this purpose, use our Get create account model to figure out what payload is required for account creation. Next, call the Create account endpoint to create the new account.

codatLending.loanWriteback.accounts.create({
accountingAccount: {
currency: "USD",
currentBalance: 0,
description: "Invoices the business has issued but has not yet collected payment on.",
fullyQualifiedCategory: "Asset.Current",
fullyQualifiedName: "Cash On Hand",
name: "Accounts Receivable",
nominalCode: "610",
status: AccountStatus.Active,
type: AccountType.Asset,

},
companyId: "8a210b68-6988-11ed-a1eb-0242ac120002",
connectionId: "2e9d2c44-f675-40ba-8049-353bfcb5e171",
}).then((res: CreateAccountResponse) => {
if (res.statusCode == 200) {
// handle response
}
});

In response, you will receive account creation details which you can display to your customer. Similarly, store the id and use it in future transactions.

Deposit the loan

Once you receive the configuration information, you are ready to deposit funds into the borrower's bank account. This is also known as loan drawdown, and it is a two-step process:

  1. Create a bank transaction depositing the amount into the lender's bank account to make the funds available for drawdown.

  2. Create a transfer from the lender's bank account to the borrower's bank account.

Create bank transaction

To record the loan deposit into the lender's bank account:

  1. Get the create bank account transactions model to determine the parameters required for transaction creation.

  2. Create bank account transactions to deposit the amount into the lender's bank account.

We provided example bank transaction creation payloads in the snippets below:

codatLending.loanWriteback.bankTransactions.create({
accountingCreateBankTransactions: {
accountId: lendersBankAccount.Id, // lender's virtual bank account id you would have stored from the configuration step
transactions: [
{
id: transactionId, // some unique identifier of the bank transaction
amount: amount, // amount to pay
date: date time now,
description: description,
},
],
},
accountId: lendersBankAccount.Id,
companyId: "8a210b68-6988-11ed-a1eb-0242ac120002",
connectionId: "2e9d2c44-f675-40ba-8049-353bfcb5e171",
}).then((res: CreateBankTransactionsResponse) => {
if (res.statusCode == 200) {
// handle response
}
});

Create transfer

Following Codat's async record creation process, use the Get create transfer model endpoint to determine the transfer request parameters.

Next, call the Create transfer endpoint to transfer the money from the lender's bank account to the borrower's bank account. Note that you are performing a transfer from the lender's account Id to the borrower's account Id.

codatLending.loanWriteback.transfers.create({
accountingTransfer: {
date: date time now,
from: {
accountRef: {
id: lendersBankAccount.Id,
},
amount: amount,
currency: "GBP",
},
to: {
accountRef: {
id: borrowersBankAccountId,
},
amount: amount,
currency: "GBP",
},
},
companyId: "8a210b68-6988-11ed-a1eb-0242ac120002",
connectionId: "2e9d2c44-f675-40ba-8049-353bfcb5e171",
}).then((res: CreateTransferResponse) => {
if (res.statusCode == 200) {
// handle response
}
});

Repay the loan

Based on the loan's terms and conditions, the borrower will preiodically repay the lender the loan amount and any associated fees. To reflect that programmatically:

  1. For each repayment, create a transfer from the borrower's bank account to the lender's.

  2. To record interest or fees, create a direct cost.

  3. Create bank transactions to deposit the repayment into the lender's account.

Repeat these steps every time a repayment is made. For example, if the borrower took out a loan of £1000 and agreed on a loan charge of 20%, the total amount due comes to £1200. With a 3-month equal instalment repayment plan, the borrower would pay back £400 each month. This means a transfer of £320 to represent the payment, a direct cost of £80 to record the fees, and a bank transaction of £400 to reduce the liability to the lender.

Create bank transaction

Use the Create bank account transactions endpoint again to deposit the total amount (including the repayment, fees, and any interest) into the lender's bank account.

codatLending.loanWriteback.bankTransactions.create({
accountingCreateBankTransactions: {
accountId: lendersBankAccount.id, // lender's virtual bank account id you would have stored from the configuration step
transactions: [
{
id: transactionId, // some unique identifier of the bank transaction
amount: amount, // amount to pay
date: date time now,
description: description,
},
],
},
accountId: lendersBankAccount.Id,
companyId: "8a210b68-6988-11ed-a1eb-0242ac120002",
connectionId: "2e9d2c44-f675-40ba-8049-353bfcb5e171",
}).then((res: CreateBankTransactionsResponse) => {
if (res.statusCode == 200) {
// handle response
}
});

Create transfer

Next, use the Create transfer endpoint again - this time to record the loan repayment amount. Note that you are performing a transfer from the borrower's account Id to the lender's account Id.

codatLending.loanWriteback.transfers.create({
accountingTransfer: {
date: date time now,
from: {
accountRef: {
id: borrowersBankAccountId,
},
amount: amount,
currency: "GBP",
},
to: {
accountRef: {
id: lendersBankAccount.id,
},
amount: amount,
currency: "GBP",
},
},
companyId: "8a210b68-6988-11ed-a1eb-0242ac120002",
connectionId: "2e9d2c44-f675-40ba-8049-353bfcb5e171",
}).then((res: CreateTransferResponse) => {
if (res.statusCode == 200) {
// handle response
}
});

Create direct cost

Finally, check the Get create direct cost model, then use the Create direct cost endpoint to capture the amount of fees or interest incurred by the borrower.

codatLending.loanWriteback.directCosts.create({
accountingDirectCost: {
contactRef: {
dataType: "suppliers",
id: supplier.Id,
},
currency: "GBP",
issueDate: date time now,
lineItems: [
{
accountRef: {
id: expenseAccount.id,
},
description: "Fees and/or interest",
quantity: 1,
taxAmount: 0,
unitAmount: directCostAmount,
},
],
paymentAllocations: [
{
allocation: {
totalAmount: directCostAmount,
},
payment: {
accountRef: {
id: depositBankAccountId,
},
},
},
],
taxAmount: 0.0,
totalAmount: directCostAmount,
},
companyId: "8a210b68-6988-11ed-a1eb-0242ac120002",
connectionId: "2e9d2c44-f675-40ba-8049-353bfcb5e171",
}).then((res: CreateDirectCostResponse) => {
if (res.statusCode == 200) {
// handle response
}
});

As a result of this process, your borrower will have the loan writeback reflected correctly in their accounting platform, saving their time on reconciliation and making sure they (and you!) have clarity on the state of the loan.

Recap

In this guide, you have learned:

  • What is loan writeback and what it's used for.
  • How to map and configure the loan writeback solution.
  • How to perform the necessary postings using Codat's endpoints.


Was this page useful?
❤️
👍
🤔
👎
😭