SendLoanOfferCommand
Overview​
Sends a loan offer through the configured cmdBankLingoSendLoanOfferLetter command chain and records the action in the loan approval history. It is normally called after the loan reaches FullyApproved and before disbursement.
Command Structure​
{
"cmd": "SendLoanOfferCommand",
"data": "JSON string containing loanRequestId, comment, and loanPurpose"
}
Parameters​
| Parameter | Type | Required | Description |
|---|---|---|---|
loanRequestId | long | Yes | Loan application (LoanQuote) ID |
comment | string | No | Note written to the approval history after successful delivery |
loanPurpose | string | No | Backward-compatible fallback for legacy loan rows. New loan requests use the purpose persisted on LoanQuote. |
Command-chain context​
Before executing cmdBankLingoSendLoanOfferLetter, the command loads the customer, address, guarantors, branch hierarchy, deductions, and persisted repayment schedules. The object is serialized with camel-case property names and supplied to the chain as context.
The following example documents the complete context contract:
{
"requestedAmount": 500000.00,
"approvedAmount": 450000.00,
"totalDeductions": 5000.00,
"netDisbursementAmount": 445000.00,
"loanRequest": {
"id": 110,
"userId": 27,
"loanAmount": 450000.00,
"applicantEmail": "customer@example.com",
"applicantName": "Jane Customer",
"loanPurpose": "Business expansion"
},
"currency": "NGN",
"loanQuote": {
"loanAmount": 450000.00,
"loanProductCode": "PR-1",
"loanTenor": 6,
"interestRate": 4.0,
"repaymentAmount": 78000.00,
"displayName": "Payroll Loan",
"currencyCode": "NGN",
"requestedAmount": 500000.00,
"approvedAmount": 450000.00,
"totalDeductions": 5000.00,
"netDisbursementAmount": 445000.00
},
"guarantors": [
{
"id": 15,
"loanQuoteId": 110,
"name": "John Guarantor",
"emailAddress": "guarantor@example.com",
"mobileNumber": "08000000000",
"address": "Lagos",
"homeAddress": "Home address",
"officeAddress": "Office address",
"designation": "Manager",
"relationship": "Colleague",
"verificationStatus": 1,
"verificationComment": "Verified",
"dateOfBirth": "1985-04-10T00:00:00"
}
],
"deductions": [
{
"id": 9,
"code": "PROC_FEE",
"name": "Processing fee",
"deduction": 1.0,
"calculatedAmount": 5000.00,
"deductionCalculation": 0,
"deductionCalculationDesc": "Percentage",
"createdAt": "2026-09-23T10:00:00Z",
"createdBy": "loan.officer"
}
],
"schedule": [
{
"id": 501,
"installmentNumber": 1,
"paymentNumber": 1,
"dueDate": "2026-10-31T00:00:00",
"principalAmount": 75000.00,
"principal": 75000.00,
"interestAmount": 3000.00,
"interest": 3000.00,
"totalDue": 78000.00,
"total": 78000.00,
"outstandingBalance": 375000.00,
"status": "Pending",
"paidDate": null,
"amountPaid": null
}
],
"repaymentSchedule": {
"loanQuoteId": 110,
"hasSchedule": true,
"totalInstallments": 6,
"totalAmountDue": 468000.00,
"totalAmountPaid": 0.00,
"outstandingAmount": 468000.00
},
"settlementDetails": {
"id": 801,
"loanQuoteId": 110,
"isConfigured": true,
"loanAccountReference": "0110",
"settlementOption": 1,
"settlementOptionDescription": "Direct Settlement Account",
"providerCode": "EMTBO",
"providerName": "Empire Trust (Bank One)",
"virtualAccountNumber": null,
"accountNumber": "1100360574",
"createdAt": "2026-09-23T10:00:00Z",
"updatedAt": "2026-09-23T11:00:00Z"
},
"disbursementDetails": {
"id": 901,
"loanQuoteId": 110,
"isConfigured": true,
"bankId": 12,
"bankCode": "000013",
"bankName": "Example Bank",
"accountNumber": "0123456789",
"accountName": "Jane Customer",
"providerCode": "BANKLINGO",
"currencyCode": "NGN",
"transactionReference": "DISB-110",
"providerMessage": "Pending",
"disbursementMode": 2,
"disbursementModeDescription": "Bank Transfer",
"disbursementState": 1,
"disbursementStateDescription": "Pending Disbursement",
"disbursementAmount": 450000.00,
"disbursementFee": 0.00,
"disbursementDate": "2026-09-25T00:00:00",
"disbursedById": 27,
"thirdPartySettlementAccount": null,
"thirdPartySettlementBank": null,
"thirdPartySettlementBankCode": null,
"thirdPartySettlementReference": null,
"createdAt": "2026-09-23T10:00:00Z",
"updatedAt": "2026-09-23T11:00:00Z"
},
"product": {
"code": "PR-1",
"name": "Payroll Loan",
"currencyCode": "NGN"
},
"customerAddress": {
"id": 31,
"addressLine1": "10 Example Street",
"addressLine2": "Ikeja",
"city": "Lagos",
"state": "Lagos",
"country": "Nigeria",
"postalCode": "100001"
},
"userDetail": {
"id": 27,
"emailAddress": "customer@example.com",
"displayName": "Jane Customer",
"firstName": "Jane",
"lastName": "Customer",
"mobileNumber": "08000000000",
"mobileCountryCode": "+234"
}
}
Repayment schedule contract​
scheduleis ordered byinstallmentNumber.paymentNumber,principal,interest, andtotalare template-friendly aliases forinstallmentNumber,principalAmount,interestAmount, andtotalDue.repaymentSchedulecontains totals and ahasScheduleflag; it does not duplicate the installment array.- If no repayment schedule has been generated,
scheduleis[],hasScheduleisfalse, and all summary totals are zero. - Only plain context values are sent to the command engine. EF navigation properties and the
LoanQuoteback-reference are excluded.
Settlement and disbursement contracts​
UpdateLoanSettlementDetailsCommandpersists settlement fields on[BPMLoanSelfService].[SelfServiceLoanCollectionInformation], linked byLoanQuoteId.SetDisbursementDetailsCommandpersists the beneficiary account, bank, mode, amount, state, and date on[BPMLoanSelfService].[SelfServiceDisbursementInformation], also linked byLoanQuoteId.settlementDetailsanddisbursementDetailsexpose the most recently updated linked records as plain values tocmdBankLingoSendLoanOfferLetter.settlementDetails.providerNameis resolved from the configured virtual-account provider usingproviderCode.disbursementDetails.bankNameis resolved from the administration bank list usingbankCode(matching either the internal or NIBSS bank code).loanRequest.loanPurposecomes from the purpose persisted onLoanQuote; the command input remains only as a fallback for pre-migration records.isConfigureddistinguishes a pre-created/default record from details that have actually been configured.- If a linked record does not exist, its corresponding context property is
null. - Offer letters are commonly sent before disbursement setup. In that sequence,
disbursementDetailscan exist withisConfigured: falseand its initial account values; templates should checkisConfiguredbefore presenting the details as final.
Example document-template loop:
Processing sequence​
- Load the loan quote and related customer, address, guarantor, branch, schedule, settlement, and disbursement data.
- Calculate deductions, approved amount, and net disbursement amount.
- Build the complete command-chain context shown above.
- Resolve and execute
cmdBankLingoSendLoanOfferLetter. - Record the
Send Offer Letter/Sentapproval-history entry only when the configured chain succeeds. - Return the command-chain response unchanged.
If the command chain is not configured, the command returns CONFIG_MISSING and includes the prepared context in the diagnostic response.
Lifecycle Position​
FullyApproved
|
SendLoanOfferCommand
|
SetDisbursementDetailsCommand
|
DisburseLoanRequestCommand