Adjust disbursement date
curl --request POST \
--url https://api-staging.bsa.ai/v1/loans/external/{loan_external_id}/adjust-disbursement-date \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"actualDisbursementDate": "<string>",
"note": "<string>"
}
'import requests
url = "https://api-staging.bsa.ai/v1/loans/external/{loan_external_id}/adjust-disbursement-date"
payload = {
"actualDisbursementDate": "<string>",
"note": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({actualDisbursementDate: '<string>', note: '<string>'})
};
fetch('https://api-staging.bsa.ai/v1/loans/external/{loan_external_id}/adjust-disbursement-date', 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-staging.bsa.ai/v1/loans/external/{loan_external_id}/adjust-disbursement-date",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'actualDisbursementDate' => '<string>',
'note' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$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-staging.bsa.ai/v1/loans/external/{loan_external_id}/adjust-disbursement-date"
payload := strings.NewReader("{\n \"actualDisbursementDate\": \"<string>\",\n \"note\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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.post("https://api-staging.bsa.ai/v1/loans/external/{loan_external_id}/adjust-disbursement-date")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"actualDisbursementDate\": \"<string>\",\n \"note\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api-staging.bsa.ai/v1/loans/external/{loan_external_id}/adjust-disbursement-date")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"actualDisbursementDate\": \"<string>\",\n \"note\": \"<string>\"\n}"
response = http.request(request)
puts response.read_bodyLoans
Adjust disbursement date
Shift an active loan’s disbursal date when the partner-side wallet credit landed later than the LMS recorded.
POST
/
v1
/
loans
/
external
/
{loan_external_id}
/
adjust-disbursement-date
Adjust disbursement date
curl --request POST \
--url https://api-staging.bsa.ai/v1/loans/external/{loan_external_id}/adjust-disbursement-date \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"actualDisbursementDate": "<string>",
"note": "<string>"
}
'import requests
url = "https://api-staging.bsa.ai/v1/loans/external/{loan_external_id}/adjust-disbursement-date"
payload = {
"actualDisbursementDate": "<string>",
"note": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({actualDisbursementDate: '<string>', note: '<string>'})
};
fetch('https://api-staging.bsa.ai/v1/loans/external/{loan_external_id}/adjust-disbursement-date', 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-staging.bsa.ai/v1/loans/external/{loan_external_id}/adjust-disbursement-date",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'actualDisbursementDate' => '<string>',
'note' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$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-staging.bsa.ai/v1/loans/external/{loan_external_id}/adjust-disbursement-date"
payload := strings.NewReader("{\n \"actualDisbursementDate\": \"<string>\",\n \"note\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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.post("https://api-staging.bsa.ai/v1/loans/external/{loan_external_id}/adjust-disbursement-date")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"actualDisbursementDate\": \"<string>\",\n \"note\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api-staging.bsa.ai/v1/loans/external/{loan_external_id}/adjust-disbursement-date")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"actualDisbursementDate\": \"<string>\",\n \"note\": \"<string>\"\n}"
response = http.request(request)
puts response.read_bodyShifts the effective disbursal date on an
The adjustment also moves the loan’s
Active loan. Use this when
the partner’s wallet credit was delayed (e.g. pending → confirmed two
days later) and the LMS-recorded disbursement date no longer matches
the day the customer actually received the money.
Interest re-accrues from the new date, and the
repaymentSchedule is recalculated so the next
due-date math matches reality.
A loan with recorded repayments is rejected with
409 aborted
(adjust_has_repayments). The adjustment reverses and re-books the
disbursement, which would cascade-reverse any repayments — so erasing
them must be an explicit choice. Have the repayment(s) reversed by the
operator team first, then adjust.A loan that already carries a posted overdue penalty is likewise
rejected with 409 aborted (adjust_has_penalty): moving the
disbursement date would move dueDateTime under a penalty that has
already been charged. Waive or reverse the penalty first.disbursedAt and
dueDateTime: the due instant shifts
by whole days to the corrected date at the original time of day, in step
with the LMS due date, so the penalty fires relative to when the customer
actually had the money.
Date constraints
- The new date must not be in the future — pass today or any past day.
- The new date must not be before the loan’s approval date. Because
POST /v1/loansapproves on the same day it disburses, the new date must be on or after the original disbursement date.
Path parameters
string
required
The loan’s externalId. On the
/v1/loans/{loan_id}/adjust-disbursement-date
form, this is the numeric LMS id instead.Request body
string
required
ISO-8601
YYYY-MM-DD. The corrected disbursal date.string
Optional. Reason for the adjustment, captured in the LMS audit log.
Examples
# By loan externalId (recommended)
curl -sf -X POST "$BASE/v1/loans/external/loan-ext-12345/adjust-disbursement-date" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"actualDisbursementDate": "2026-05-30",
"note": "Wallet credit confirmed 2 days after original disbursement"
}'
# Same effect, by LMS id
curl -sf -X POST "$BASE/v1/loans/501/adjust-disbursement-date" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"actualDisbursementDate": "2026-05-30"}'
Response
200 OK returns the loan object with the new
disbursal date, recomputed schedule, and adjusted next-due fields.
How it works
The wrapper performsundo-disbursal then re-disburse against the
LMS with the corrected date. These are two separate LMS commands, so
the wrapper makes them atomic from the partner’s point of view: it
records the current disbursement date first, and if re-disbursing at the
new date fails it automatically rolls the loan back to the original
date. Either the adjustment succeeds (loan Active, new schedule), or
it fails cleanly with the loan left in its original Active state — the
loan is never stranded in Approved.
The only exception is the rare case where the re-disbursal and the
automatic rollback both fail; the response is then a 500 whose message
states the loan is in Approved and names the original date to
re-disburse manually.
Errors
| Code | When |
|---|---|
not_found | No loan with that id or externalId (loan_not_found) |
failed_precondition | The loan has no disbursement to adjust (loan_no_disbursement), or the LMS refused the date — in the future, or before the loan’s approval date (domain_rule_violation) |
aborted | The loan has recorded repayments (adjust_has_repayments) or a posted overdue penalty (adjust_has_penalty) — clear those before adjusting the date |
invalid_argument | actualDisbursementDate missing or not ISO YYYY-MM-DD (validation_error) |
internal | Re-disbursal failed and automatic rollback also failed — loan is in Approved and needs manual re-disbursal (message names the original date) |

