Overview
The Loan Disbursement Workflow in Agatabo describes the process of creating loans, ensuring proper validation, and recording disbursement with accurate accounting.Current implementation: Loan creation and disbursement happen simultaneously in one API call. The loan is created with status
ACTIVE and disbursed immediately.PENDING status exists in the database schema but is not currently used by the API. A multi-stage approval workflow may be added in future versions.Workflow Overview
Stage 1: Pre-Loan Preparation
Who: Loan Officer or Administrator Before creating loan in system:Verify member eligibility
- Member has required savings balance (typically 20-30% of loan amount)
- Member has no existing ACTIVE or DEFAULTED loans
- Member has “member” role assigned (required for loan eligibility)
- Member is active in organization
Assess repayment capacity
- Review member’s income sources
- Check member’s loan history (past defaults?)
- Verify member can afford installment payments
- Assess overall creditworthiness
Collect securities
- Guarantors: Other active members who co-sign
- Collateral: Physical assets (property, equipment) with estimated values
- Savings requirement: Minimum savings balance member must maintain
- Documents: Photos, titles, appraisals, agreements
- ✅ Member eligible for loan
- ✅ Securities adequate to cover loan amount
- ✅ Loan terms comply with organization policies
- ✅ Required documentation collected
- ✅ Loan committee approval obtained (if required by organization)
Stage 2: Loan Creation & Disbursement
Who: User withloans:write permission
API Endpoint: POST /loans
Request
| Field | Type | Description |
|---|---|---|
organizationUserId | string | Borrower (must have “member” role, be active, no existing ACTIVE/DEFAULTED loans) |
principalAmount | number | Loan amount |
period | number | Loan term in months |
interestRate | number | Interest rate percentage |
interestRateType | enum | ”MONTHLY” or “YEARLY” |
interestCalculationType | enum | ”SIMPLE”, “COMPOUND”, or “REDUCING_BALANCE” |
startDate | string | Loan start date (ISO format) |
bankAccountId | string | Bank account for disbursement |
| Field | Type | Default | Description |
|---|---|---|---|
interestPaymentTiming | enum | From org settings | ”IN_ADVANCE” or “WITH_INSTALLMENTS” |
installmentType | enum | From org settings | ”EQUAL_PRINCIPAL” or “EQUAL_TOTAL” |
paymentAllocationOrder | enum | From org settings | ”INTEREST_FIRST”, “PRINCIPAL_FIRST”, or “PROPORTIONAL” |
purpose | string | null | Loan purpose description |
loanSecurities | array | [] | Guarantors, collateral, savings requirements |
disbursementFeeType | enum | From org settings | ”PERCENTAGE” or “FIXED_AMOUNT” |
disbursementFeePercentage | number | From org settings | Fee percentage (e.g., 1.0 = 1%) |
disbursementFeeFixedAmount | number | From org settings | Fixed fee amount |
bankChargeAmount | number | null | Bank transfer charges |
skipNegativeBalanceCheck | boolean | false | Allow negative balances (use with caution) |
What Happens Automatically
When loan is created, the system performs these actions atomically in one database transaction:Validate member eligibility
- Member exists in organization
- Member is active
- Member has “member” role assigned
- Member has no existing ACTIVE or DEFAULTED loans
Validate organization policies
- Principal amount within configured limits
- Period within maximum allowed (if configured)
- Principal ≤ (member savings × max_loan_percentage)
- Amount scale matches organization currency settings
Validate securities
- Total security value ≥ loan amount
- Guarantors are active members
- Savings requirement doesn’t exceed member’s available savings
- All security values use correct decimal precision
Calculate installments
- Computes EMI (Equated Monthly Installment)
- Generates installment schedule
- Calculates total interest
- Applies configured rounding rules
Create loan record
- Status set to ACTIVE (not PENDING)
- Loan assigned unique account number
- Installment schedule locked
- Securities stored as JSON
Create ledger accounts
- LOAN_RECEIVABLE account (principal tracking)
- INTEREST_RECEIVABLE account (interest tracking)
- PENALTY_RECEIVABLE account (penalty tracking)
Post disbursement journal entry
Basic entry (interest with installments, fee from cash):Variations:
- If fee deducted from savings: Separate DISBURSEMENT_FEE_PAYMENT entry
- If interest paid in advance from savings: Separate INTEREST_PAID_IN_ADVANCE entry
- If interest paid in advance from cash: INTEREST_RECEIVABLE not debited, CASH reduced by interest
- If bank charge: Additional BANK_CHARGE entry
Response
Success (201 Created):Errors
| Status | Error | Cause |
|---|---|---|
| 400 | ”x-idempotency-key header is required” | Missing idempotency key |
| 404 | ”Organization user not found, inactive, or does not belong to this organization” | Invalid organizationUserId or member not active |
| 409 | ”Organization user already has an active or defaulted loan” | Member already has ACTIVE or DEFAULTED loan |
| 400 | ”Loan period cannot exceed X month(s)“ | Period exceeds organization max_period setting |
| 400 | ”Principal exceeds max allowed from max loan percentage rule” | Principal > (savings × max_loan_percentage) |
| 400 | ”Insufficient security coverage” | Total securities < loan amount |
| 400 | Various validation errors | Invalid data, decimal precision issues, etc. |
Stage 3: Post-Disbursement
Who: Loan Officer, TreasurerImmediate Actions
Disburse funds to member
Method 1: Deduct from savings (if fee/interest deducted from savings):
- System already deducted from member’s savings account
- Member receives cash: Principal - (fee if from cash) - (interest if from cash and in advance)
- Count cash with member present
- Member signs receipt
- Transfer from organization bank account to member’s account
- Record bank reference number
- Amount: Principal - (fees/interest deducted from cash)
- Withdraw from organization bank
- Give cash to member
- Member signs receipt
File loan documentation
- Signed loan agreement
- Collateral documents (titles, appraisals, photos)
- Guarantor agreements
- Disbursement receipt
- Security documents
Provide member with payment schedule
- Export installment schedule from system
- Print and give to member
- Explain payment methods
- Confirm due dates understood
- Provide treasurer contact information
Ongoing Monitoring
Track payments
- Monitor installment due dates
- Send reminders before due dates
- Record payments promptly using
POST /loans/{loanId}/repay - Update payment status (on-time, late, missed)
Manage delinquencies
- Identify overdue payments immediately
- Follow up with member (phone call, visit)
- Apply penalties per organization policy
- Escalate to loan committee if needed
- Consider restructuring for hardship cases
Disbursement Fee & Interest Payment Options
Fee Deduction Options
Option 1: Fee deducted from cash (default):disbursementFeeFromSavings: true):
Interest Payment Timing Options
Option 1: Interest paid with installments (default):interestPaymentTiming: "IN_ADVANCE"):
interestPaymentTiming: "IN_ADVANCE" and advanceInterestFromSavings: true):
Configuration from Organization Settings
Several loan parameters default to organization-wide settings (fromloanSettings field):
| Setting | Default | Description |
|---|---|---|
interest_payment_timing | ”WITH_INSTALLMENTS” | When interest is paid |
installment_type | ”EQUAL_PRINCIPAL” | How installments calculated |
payment_allocation_order | ”INTEREST_FIRST” | How payments allocated |
max_loan_percentage | (none) | Max loan as multiple of savings (e.g., 3.0 = 3× savings) |
max_period | (none) | Maximum loan term in months |
disbursement_fee_percentage | 0 | Default fee percentage |
disbursement_fee_type | ”PERCENTAGE" | "PERCENTAGE” or “FIXED_AMOUNT” |
disbursement_fee_fixed_amount | 0 | Fixed fee amount |
deduct_disbursement_fee_from_savings | false | Deduct fee from savings instead of cash |
deduct_advance_interest_from_savings | false | Deduct advance interest from savings instead of cash |
Complete Workflow Checklist
Pre-Creation
- ✅ Member eligibility verified (savings, loan history, active status)
- ✅ Securities documented (guarantors, collateral, documents)
- ✅ Loan terms calculated and comply with policies
- ✅ Required documents collected
- ✅ Loan committee approval obtained (if required by organization)
- ✅ Member understands repayment terms
During Creation (via API)
- ✅
x-idempotency-keyheader provided - ✅ All required fields in request body
- ✅ Loan amount within policy limits
- ✅ Securities adequate to cover loan
- ✅ Member has no existing ACTIVE/DEFAULTED loans
- ✅ API call succeeds (201 response)
Post-Disbursement
- ✅ Funds disbursed to member (cash or bank transfer)
- ✅ Member signs disbursement receipt
- ✅ Loan documents filed
- ✅ Payment schedule provided to member
- ✅ Member added to payment reminder list
- ✅ First payment reminder scheduled
Ongoing
- ✅ Payments recorded promptly
- ✅ Overdue payments followed up immediately
- ✅ Penalties applied per policy
- ✅ Loan status updated as needed (COMPLETED, DEFAULTED)
Common Issues
Member already has active loan
Member already has active loan
Error: “Organization user already has an active or defaulted loan”Cause: Member currently has a loan with status ACTIVE or DEFAULTEDSolution:
- Wait until existing loan is fully repaid (status = COMPLETED)
- Or default the existing loan if appropriate (POST /loans//default)
- Cannot have multiple simultaneous loans per member
Insufficient securities
Insufficient securities
Error: “Insufficient security coverage”Cause: Total value of securities < loan principal amountSolution:
- Add more guarantors
- Increase collateral value
- Reduce loan amount
- Require higher savings balance
Principal exceeds max allowed
Principal exceeds max allowed
Error: “Principal exceeds max allowed from max loan percentage rule”Cause: Loan amount > (member savings × max_loan_percentage)Example: Savings = 100,000, max_loan_percentage = 3.0, max allowed = 300,000Solution:
- Reduce loan amount
- Ask member to save more first
- Request organization to increase max_loan_percentage setting
Period exceeds maximum
Period exceeds maximum
Error: “Loan period cannot exceed X month(s)”Cause: Loan period > organization’s max_period settingSolution:
- Reduce loan period
- Request organization to increase max_period setting
Need to cancel loan after creation
Need to cancel loan after creation
Situation: Loan created but member wants to cancelIf just created (same day):
- Member can repay full principal immediately
- Record payment: POST /loans//repay
- Loan status changes to COMPLETED
- Member must repay according to schedule
- Or negotiate restructuring with loan officer
- Cannot delete disbursed loan (funds already given)
Loan settings not applying
Loan settings not applying
Problem: Defaults from organization settings not being usedCheck:
- Are organization
loanSettingsconfigured? - Do request body fields override settings?
- Verify settings with GET /organizations/
Future Enhancement: Multi-Stage Workflow
PENDING status exists in the database but is not currently used. A future version may implement:- POST /loans with status=PENDING (draft loan application)
- PUT /loans//approve endpoint to change PENDING → ACTIVE
- DELETE /loans/ to cancel PENDING loans
- Workflow states and approval process
- Loan applications submitted for review
- Committee approval before disbursement
- Separation of application and disbursement
- Cancellation of unapproved loans
Related Topics
Creating Loans
Detailed loan API documentation
Loan Rules
Interest calculations and installment types
Recording Payments
How to record loan repayments
Loans Outstanding
Monitor loan portfolio