cURL
curl --request PUT \
--url https://api.finosu.com/loans/{id} \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"current_balance": 123,
"originalFundedAmount": 123,
"originalTotalOwed": 123,
"interestRate": 123,
"apr": 123,
"originationDate": "2023-12-25",
"startDate": "2023-12-25",
"totalBalance": 123,
"payoffAmount": 123,
"totalPayoffAmount": 123,
"balanceAtTransfer": 123,
"preTransferPayments": 123,
"dueDate": "2023-12-25",
"stopServicingReason": "<string>",
"isSettled": true,
"chargeoffDate": "2023-12-25",
"chargeoffAmount": 123,
"autopayEnabled": true,
"noCallReason": "<string>",
"noTextReason": "<string>",
"noEmailReason": "<string>",
"isVisibleInBorrowerPortal": true,
"daysPastDueAtBoarding": 123,
"daysSinceOriginationAtBoarding": 123,
"externalLmsMetadata": {},
"fraud": true
}
'import requests
url = "https://api.finosu.com/loans/{id}"
payload = {
"current_balance": 123,
"originalFundedAmount": 123,
"originalTotalOwed": 123,
"interestRate": 123,
"apr": 123,
"originationDate": "2023-12-25",
"startDate": "2023-12-25",
"totalBalance": 123,
"payoffAmount": 123,
"totalPayoffAmount": 123,
"balanceAtTransfer": 123,
"preTransferPayments": 123,
"dueDate": "2023-12-25",
"stopServicingReason": "<string>",
"isSettled": True,
"chargeoffDate": "2023-12-25",
"chargeoffAmount": 123,
"autopayEnabled": True,
"noCallReason": "<string>",
"noTextReason": "<string>",
"noEmailReason": "<string>",
"isVisibleInBorrowerPortal": True,
"daysPastDueAtBoarding": 123,
"daysSinceOriginationAtBoarding": 123,
"externalLmsMetadata": {},
"fraud": True
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.put(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PUT',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
current_balance: 123,
originalFundedAmount: 123,
originalTotalOwed: 123,
interestRate: 123,
apr: 123,
originationDate: '2023-12-25',
startDate: '2023-12-25',
totalBalance: 123,
payoffAmount: 123,
totalPayoffAmount: 123,
balanceAtTransfer: 123,
preTransferPayments: 123,
dueDate: '2023-12-25',
stopServicingReason: '<string>',
isSettled: true,
chargeoffDate: '2023-12-25',
chargeoffAmount: 123,
autopayEnabled: true,
noCallReason: '<string>',
noTextReason: '<string>',
noEmailReason: '<string>',
isVisibleInBorrowerPortal: true,
daysPastDueAtBoarding: 123,
daysSinceOriginationAtBoarding: 123,
externalLmsMetadata: {},
fraud: true
})
};
fetch('https://api.finosu.com/loans/{id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.finosu.com/loans/{id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => json_encode([
'current_balance' => 123,
'originalFundedAmount' => 123,
'originalTotalOwed' => 123,
'interestRate' => 123,
'apr' => 123,
'originationDate' => '2023-12-25',
'startDate' => '2023-12-25',
'totalBalance' => 123,
'payoffAmount' => 123,
'totalPayoffAmount' => 123,
'balanceAtTransfer' => 123,
'preTransferPayments' => 123,
'dueDate' => '2023-12-25',
'stopServicingReason' => '<string>',
'isSettled' => true,
'chargeoffDate' => '2023-12-25',
'chargeoffAmount' => 123,
'autopayEnabled' => true,
'noCallReason' => '<string>',
'noTextReason' => '<string>',
'noEmailReason' => '<string>',
'isVisibleInBorrowerPortal' => true,
'daysPastDueAtBoarding' => 123,
'daysSinceOriginationAtBoarding' => 123,
'externalLmsMetadata' => [
],
'fraud' => true
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.finosu.com/loans/{id}"
payload := strings.NewReader("{\n \"current_balance\": 123,\n \"originalFundedAmount\": 123,\n \"originalTotalOwed\": 123,\n \"interestRate\": 123,\n \"apr\": 123,\n \"originationDate\": \"2023-12-25\",\n \"startDate\": \"2023-12-25\",\n \"totalBalance\": 123,\n \"payoffAmount\": 123,\n \"totalPayoffAmount\": 123,\n \"balanceAtTransfer\": 123,\n \"preTransferPayments\": 123,\n \"dueDate\": \"2023-12-25\",\n \"stopServicingReason\": \"<string>\",\n \"isSettled\": true,\n \"chargeoffDate\": \"2023-12-25\",\n \"chargeoffAmount\": 123,\n \"autopayEnabled\": true,\n \"noCallReason\": \"<string>\",\n \"noTextReason\": \"<string>\",\n \"noEmailReason\": \"<string>\",\n \"isVisibleInBorrowerPortal\": true,\n \"daysPastDueAtBoarding\": 123,\n \"daysSinceOriginationAtBoarding\": 123,\n \"externalLmsMetadata\": {},\n \"fraud\": true\n}")
req, _ := http.NewRequest("PUT", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.put("https://api.finosu.com/loans/{id}")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"current_balance\": 123,\n \"originalFundedAmount\": 123,\n \"originalTotalOwed\": 123,\n \"interestRate\": 123,\n \"apr\": 123,\n \"originationDate\": \"2023-12-25\",\n \"startDate\": \"2023-12-25\",\n \"totalBalance\": 123,\n \"payoffAmount\": 123,\n \"totalPayoffAmount\": 123,\n \"balanceAtTransfer\": 123,\n \"preTransferPayments\": 123,\n \"dueDate\": \"2023-12-25\",\n \"stopServicingReason\": \"<string>\",\n \"isSettled\": true,\n \"chargeoffDate\": \"2023-12-25\",\n \"chargeoffAmount\": 123,\n \"autopayEnabled\": true,\n \"noCallReason\": \"<string>\",\n \"noTextReason\": \"<string>\",\n \"noEmailReason\": \"<string>\",\n \"isVisibleInBorrowerPortal\": true,\n \"daysPastDueAtBoarding\": 123,\n \"daysSinceOriginationAtBoarding\": 123,\n \"externalLmsMetadata\": {},\n \"fraud\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.finosu.com/loans/{id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"current_balance\": 123,\n \"originalFundedAmount\": 123,\n \"originalTotalOwed\": 123,\n \"interestRate\": 123,\n \"apr\": 123,\n \"originationDate\": \"2023-12-25\",\n \"startDate\": \"2023-12-25\",\n \"totalBalance\": 123,\n \"payoffAmount\": 123,\n \"totalPayoffAmount\": 123,\n \"balanceAtTransfer\": 123,\n \"preTransferPayments\": 123,\n \"dueDate\": \"2023-12-25\",\n \"stopServicingReason\": \"<string>\",\n \"isSettled\": true,\n \"chargeoffDate\": \"2023-12-25\",\n \"chargeoffAmount\": 123,\n \"autopayEnabled\": true,\n \"noCallReason\": \"<string>\",\n \"noTextReason\": \"<string>\",\n \"noEmailReason\": \"<string>\",\n \"isVisibleInBorrowerPortal\": true,\n \"daysPastDueAtBoarding\": 123,\n \"daysSinceOriginationAtBoarding\": 123,\n \"externalLmsMetadata\": {},\n \"fraud\": true\n}"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"customerId": "<string>",
"amountDue": "<string>",
"dueDate": "<string>",
"current_balance": "<string>",
"type": "<string>",
"applicationStatus": "<string>",
"originalFundedAmount": "<string>",
"originalTotalOwed": "<string>",
"interestRate": "<string>",
"apr": "<string>",
"originationDate": "2023-12-25",
"startDate": "2023-12-25",
"totalBalance": "<string>",
"payoffAmount": "<string>",
"totalPayoffAmount": "<string>",
"balanceAtTransfer": "<string>",
"preTransferPayments": "<string>",
"servicingStatus": "<string>",
"stopServicingReason": "<string>",
"isSettled": true,
"chargeoffDate": "<string>",
"chargeoffAmount": "<string>",
"collectionsReportingSegment": "COLLECTIONS",
"autopayEnabled": true,
"noCallReason": "<string>",
"noTextReason": "<string>",
"noEmailReason": "<string>",
"isVisibleInBorrowerPortal": true,
"daysPastDueAtBoarding": 123,
"daysSinceOriginationAtBoarding": 123,
"externalLmsMetadata": {},
"portfolioId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"paymentPlanMinPercentage": "<string>",
"paymentPlanMaxPercentage": "<string>",
"taskType": "<string>",
"fraud": true
}{
"detail": "<string>"
}{
"detail": "<string>"
}{
"detail": "<string>"
}{
"detail": "<string>"
}Loans
Update Loan
Partially update a loan by its external reference. Only send fields that changed. amountDue is immutable.
PUT
/
loans
/
{id}
cURL
curl --request PUT \
--url https://api.finosu.com/loans/{id} \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"current_balance": 123,
"originalFundedAmount": 123,
"originalTotalOwed": 123,
"interestRate": 123,
"apr": 123,
"originationDate": "2023-12-25",
"startDate": "2023-12-25",
"totalBalance": 123,
"payoffAmount": 123,
"totalPayoffAmount": 123,
"balanceAtTransfer": 123,
"preTransferPayments": 123,
"dueDate": "2023-12-25",
"stopServicingReason": "<string>",
"isSettled": true,
"chargeoffDate": "2023-12-25",
"chargeoffAmount": 123,
"autopayEnabled": true,
"noCallReason": "<string>",
"noTextReason": "<string>",
"noEmailReason": "<string>",
"isVisibleInBorrowerPortal": true,
"daysPastDueAtBoarding": 123,
"daysSinceOriginationAtBoarding": 123,
"externalLmsMetadata": {},
"fraud": true
}
'import requests
url = "https://api.finosu.com/loans/{id}"
payload = {
"current_balance": 123,
"originalFundedAmount": 123,
"originalTotalOwed": 123,
"interestRate": 123,
"apr": 123,
"originationDate": "2023-12-25",
"startDate": "2023-12-25",
"totalBalance": 123,
"payoffAmount": 123,
"totalPayoffAmount": 123,
"balanceAtTransfer": 123,
"preTransferPayments": 123,
"dueDate": "2023-12-25",
"stopServicingReason": "<string>",
"isSettled": True,
"chargeoffDate": "2023-12-25",
"chargeoffAmount": 123,
"autopayEnabled": True,
"noCallReason": "<string>",
"noTextReason": "<string>",
"noEmailReason": "<string>",
"isVisibleInBorrowerPortal": True,
"daysPastDueAtBoarding": 123,
"daysSinceOriginationAtBoarding": 123,
"externalLmsMetadata": {},
"fraud": True
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.put(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PUT',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
current_balance: 123,
originalFundedAmount: 123,
originalTotalOwed: 123,
interestRate: 123,
apr: 123,
originationDate: '2023-12-25',
startDate: '2023-12-25',
totalBalance: 123,
payoffAmount: 123,
totalPayoffAmount: 123,
balanceAtTransfer: 123,
preTransferPayments: 123,
dueDate: '2023-12-25',
stopServicingReason: '<string>',
isSettled: true,
chargeoffDate: '2023-12-25',
chargeoffAmount: 123,
autopayEnabled: true,
noCallReason: '<string>',
noTextReason: '<string>',
noEmailReason: '<string>',
isVisibleInBorrowerPortal: true,
daysPastDueAtBoarding: 123,
daysSinceOriginationAtBoarding: 123,
externalLmsMetadata: {},
fraud: true
})
};
fetch('https://api.finosu.com/loans/{id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.finosu.com/loans/{id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => json_encode([
'current_balance' => 123,
'originalFundedAmount' => 123,
'originalTotalOwed' => 123,
'interestRate' => 123,
'apr' => 123,
'originationDate' => '2023-12-25',
'startDate' => '2023-12-25',
'totalBalance' => 123,
'payoffAmount' => 123,
'totalPayoffAmount' => 123,
'balanceAtTransfer' => 123,
'preTransferPayments' => 123,
'dueDate' => '2023-12-25',
'stopServicingReason' => '<string>',
'isSettled' => true,
'chargeoffDate' => '2023-12-25',
'chargeoffAmount' => 123,
'autopayEnabled' => true,
'noCallReason' => '<string>',
'noTextReason' => '<string>',
'noEmailReason' => '<string>',
'isVisibleInBorrowerPortal' => true,
'daysPastDueAtBoarding' => 123,
'daysSinceOriginationAtBoarding' => 123,
'externalLmsMetadata' => [
],
'fraud' => true
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.finosu.com/loans/{id}"
payload := strings.NewReader("{\n \"current_balance\": 123,\n \"originalFundedAmount\": 123,\n \"originalTotalOwed\": 123,\n \"interestRate\": 123,\n \"apr\": 123,\n \"originationDate\": \"2023-12-25\",\n \"startDate\": \"2023-12-25\",\n \"totalBalance\": 123,\n \"payoffAmount\": 123,\n \"totalPayoffAmount\": 123,\n \"balanceAtTransfer\": 123,\n \"preTransferPayments\": 123,\n \"dueDate\": \"2023-12-25\",\n \"stopServicingReason\": \"<string>\",\n \"isSettled\": true,\n \"chargeoffDate\": \"2023-12-25\",\n \"chargeoffAmount\": 123,\n \"autopayEnabled\": true,\n \"noCallReason\": \"<string>\",\n \"noTextReason\": \"<string>\",\n \"noEmailReason\": \"<string>\",\n \"isVisibleInBorrowerPortal\": true,\n \"daysPastDueAtBoarding\": 123,\n \"daysSinceOriginationAtBoarding\": 123,\n \"externalLmsMetadata\": {},\n \"fraud\": true\n}")
req, _ := http.NewRequest("PUT", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.put("https://api.finosu.com/loans/{id}")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"current_balance\": 123,\n \"originalFundedAmount\": 123,\n \"originalTotalOwed\": 123,\n \"interestRate\": 123,\n \"apr\": 123,\n \"originationDate\": \"2023-12-25\",\n \"startDate\": \"2023-12-25\",\n \"totalBalance\": 123,\n \"payoffAmount\": 123,\n \"totalPayoffAmount\": 123,\n \"balanceAtTransfer\": 123,\n \"preTransferPayments\": 123,\n \"dueDate\": \"2023-12-25\",\n \"stopServicingReason\": \"<string>\",\n \"isSettled\": true,\n \"chargeoffDate\": \"2023-12-25\",\n \"chargeoffAmount\": 123,\n \"autopayEnabled\": true,\n \"noCallReason\": \"<string>\",\n \"noTextReason\": \"<string>\",\n \"noEmailReason\": \"<string>\",\n \"isVisibleInBorrowerPortal\": true,\n \"daysPastDueAtBoarding\": 123,\n \"daysSinceOriginationAtBoarding\": 123,\n \"externalLmsMetadata\": {},\n \"fraud\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.finosu.com/loans/{id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"current_balance\": 123,\n \"originalFundedAmount\": 123,\n \"originalTotalOwed\": 123,\n \"interestRate\": 123,\n \"apr\": 123,\n \"originationDate\": \"2023-12-25\",\n \"startDate\": \"2023-12-25\",\n \"totalBalance\": 123,\n \"payoffAmount\": 123,\n \"totalPayoffAmount\": 123,\n \"balanceAtTransfer\": 123,\n \"preTransferPayments\": 123,\n \"dueDate\": \"2023-12-25\",\n \"stopServicingReason\": \"<string>\",\n \"isSettled\": true,\n \"chargeoffDate\": \"2023-12-25\",\n \"chargeoffAmount\": 123,\n \"autopayEnabled\": true,\n \"noCallReason\": \"<string>\",\n \"noTextReason\": \"<string>\",\n \"noEmailReason\": \"<string>\",\n \"isVisibleInBorrowerPortal\": true,\n \"daysPastDueAtBoarding\": 123,\n \"daysSinceOriginationAtBoarding\": 123,\n \"externalLmsMetadata\": {},\n \"fraud\": true\n}"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"customerId": "<string>",
"amountDue": "<string>",
"dueDate": "<string>",
"current_balance": "<string>",
"type": "<string>",
"applicationStatus": "<string>",
"originalFundedAmount": "<string>",
"originalTotalOwed": "<string>",
"interestRate": "<string>",
"apr": "<string>",
"originationDate": "2023-12-25",
"startDate": "2023-12-25",
"totalBalance": "<string>",
"payoffAmount": "<string>",
"totalPayoffAmount": "<string>",
"balanceAtTransfer": "<string>",
"preTransferPayments": "<string>",
"servicingStatus": "<string>",
"stopServicingReason": "<string>",
"isSettled": true,
"chargeoffDate": "<string>",
"chargeoffAmount": "<string>",
"collectionsReportingSegment": "COLLECTIONS",
"autopayEnabled": true,
"noCallReason": "<string>",
"noTextReason": "<string>",
"noEmailReason": "<string>",
"isVisibleInBorrowerPortal": true,
"daysPastDueAtBoarding": 123,
"daysSinceOriginationAtBoarding": 123,
"externalLmsMetadata": {},
"portfolioId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"paymentPlanMinPercentage": "<string>",
"paymentPlanMaxPercentage": "<string>",
"taskType": "<string>",
"fraud": true
}{
"detail": "<string>"
}{
"detail": "<string>"
}{
"detail": "<string>"
}{
"detail": "<string>"
}Overview
Partially update a loan by its external reference ID. Only the fields that changed need to be sent — omitted fields are left unchanged.Collection balance updates
UsePUT /loans/{id}, where id is your external loan ID, with the
company API key in X-API-Key and this entire JSON payload:
{ "current_balance": "905.00" }
current_balance is the outstanding balance including accrued interest. It is
an absolute USD amount, not an increment. Send no interest, timestamp, update ID,
classification, or other loan fields in this request. The snake_case spelling is
required. Decimal strings are recommended; amounts must be finite, nonnegative,
at cent precision, and no greater than 99999999.99.
Scope and stopping updates
This feed is available only to explicitly approved integrations; contact Finosu to confirm eligibility for your authenticated account before sending updates. Each request must identify a single unambiguous, active, company-owned loan. Send collection accounts only, never charge-off accounts. Your system owns the category; Finosu adds no category field and does not infer that category from storedchargeoffDate or
chargeoffAmount. Those boarding fields do not block this feed and remain unchanged.
Your integration must stop publishing once Finosu receives any full or partial payment or a
payment plan is created. Finosu also enforces this locally: uploads return 409
while money is pending, after confirmed/returned/refunded payment activity, or
after plan creation. A failed attempt that never received money does not by itself
freeze accrual. Hidden/deleted history cannot remove the freeze. These checks run
under the same loan lock used for direct payment reservations and plan creation;
partner notification delivery is not the only protection against a late upload.
The last accepted external amount becomes the enrolled loan’s local calculation
basis. Later successful payments reduce it exactly once; pending payments reduce
only today’s available-to-pay amount. Returns restore the local cash deduction
without automatically authorizing interest resumption. Accepted plan totals remain
fixed. The internal totalBalance, boarding fields and ledger transactions are
not rewritten by this feed. Loans not enrolled retain their existing calculations.
Staff currentTotalBalance remains the existing internal-ledger display; it is
not relabeled as the external amount. For reconciliation, distinguish that value from
the accepted current_balance and the borrower-payable amount after local cash.
Ordering and retries
There is no sender timestamp or idempotency key in this contract. Updates are applied in serialized server-receipt order, not source-effective order. Each accepted request records an audit snapshot, including equal amounts; retries can add audit rows but cannot add the amount to the balance. The response includes the latest acceptedcurrent_balance; totalBalance remains the independent internal field.
Your integration must serialize uploads per loan (one outstanding request), stop and discard
queued/retried uploads when a payment/plan notification arrives, and never retry an
old value after a newer one. A timeout is ambiguous; do not assume the upload failed.
Finosu cannot distinguish a delayed old value from a fresh one once updates are
allowed again. Lifecycle protection rejects packets during a freeze; it is not
an end-to-end freshness guarantee after resumption.
Resumption and rollout gates
The agreed resume cases are a Finosu-side plan cancellation, or a qualifying return after plan completion. These are not generic permission to restart uploads:- Cancellation resumption requires an explicit Finosu lifecycle signal, a durable
delivery receipt, and a fresh balance that starts a new local deduction baseline.
The historical
PaymentPlanCancelledwire contract must be reconfirmed with your integration team; its new sender is disabled until that contract/rollout setting is approved. For a v2-enrolled loan with financial history linked to any payment plan, cancellation itself returns409for manual review before changing the plan. Removing that agreement without historical plan-payment/credit accounting could overstate the payable amount. Paid-plan cancellation and later return/resumption need separate v2 accounting support; merely holding uploads is not sufficient. An unpaid current plan is not by itself eligible: any transaction history linked to any of the loan’s plans (including failed, returned, prior or deleted rows), or any loan-credit ledger history, still requires manual review. Cancellation also checks active servicing, company scope, other active plans, and processing or scheduled terminal payments. Non-enrolled loans are unchanged. - Return-based resumption is agreed intent, not operationally available in this implementation. It requires a separate implementation and confirmation of the hard/soft return mapping and which outcomes resume accrual. Neither rollout flag enables it. Existing retry recommendations are not an interest-accrual policy. Raw return codes/reasons remain unchanged.
- Before any authorized resume, discard all pre-freeze pending uploads/retries. The single-field payload cannot prove a source epoch or distinguish their lateness.
409. Returns never authorize resumption in this
implementation. Do not resume solely because a plan is inactive, a payment returned,
a webhook was queued, or a previous upload succeeded.
Finosu must deploy compatible API and payment-worker readers before enrolling
loans. Snapshot and enrollment evidence must be preserved on rollback; stop new
submissions rather than deleting it. Historical enrollment requires a reviewed
migration; this endpoint performs no schema migration or historical backfill.
New uploads are disabled by default and require separate enablement by Finosu
after reader compatibility is confirmed. Disabling uploads does not stop local
payments or existing enrolled-balance readers. Cancellation-based resumption has
its own separate rollout setting, which remains disabled until your integration
team reconfirms the cancellation signal and the gated lifecycle is accepted.
While uploads are disabled, the API returns 409 without changing the balance,
enrollment, or snapshots. Publishing this documentation does not enable either
capability or establish eligibility for your account.
Fraud
Usefraud on this endpoint because fraud belongs to one exact loan, not the customer generally.
true records the fraud claim, stops servicing for FRAUD_CLAIM, blocks all communication
channels, and cancels pending communications and scheduled payments for this loan. false
clears the fraud fact and removes only fraud-owned holds; another active compliance restriction
continues to control servicing and channel blocks. Repeating the current value is idempotent.
Omitted or null makes no change.
Do not combine fraud with servicingStatus, stopServicingReason, noCallReason,
noTextReason, noEmailReason, or isVisibleInBorrowerPortal; those fields are owned by the
fraud cascade for that request. If duplicate live records share the external loan ID, a fraud
update returns 409 rather than choosing an arbitrary loan.
Immutable Fields
The following fields cannot be changed via this endpoint:id— The external loan reference (used as the path parameter)customerId— The customer linkageamountDue— The origination amount set at creation
Servicing Status Transitions
SettingservicingStatus to NOT_SERVICING via this endpoint also cancels all scheduled payments and communications for the loan, matching the behavior of POST /loans/{id}/cancel-servicing. You can optionally include stopServicingReason (defaults to API_REQUEST).
For stopping servicing specifically, the dedicated POST /loans/{id}/cancel-servicing endpoint remains the primary path.
Request Body
Only send the fields you want to update:{
"totalBalance": 300.00,
"payoffAmount": 300.00,
"isSettled": false
}
{
"fraud": true
}
{
"servicingStatus": "NOT_SERVICING",
"stopServicingReason": "PAID_OFF"
}
Error Responses
| Status Code | Description |
|---|---|
| 422 | Invalid enum value |
| 404 | Loan not found |
| 500 | Internal server error |
Body
application/json
Fields to update
Partial update. id, customerId, amountDue cannot be changed. Collection-balance requests for explicitly approved integrations contain only current_balance (outstanding including accrued interest). No interest, timestamp, update ID or category. Payment/plan activity freezes uploads. Receipts cannot prove source freshness; see Update Loan for ordering, resume gates and rollout requirements.
Response
Loan updated