Skip to main content

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

Preparation → Creation & Validation → Automatic Disbursement → Monitoring

Stage 1: Pre-Loan Preparation

Who: Loan Officer or Administrator Before creating loan in system:
1

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
2

Assess repayment capacity

  • Review member’s income sources
  • Check member’s loan history (past defaults?)
  • Verify member can afford installment payments
  • Assess overall creditworthiness
3

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
4

Calculate loan terms

  • Determine principal amount (within organization limits)
  • Decide loan period (months)
  • Set interest rate (per organization policy)
  • Choose interest calculation method
  • Calculate estimated installments
Checklist before system entry:
  • ✅ 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 with loans:write permission API Endpoint: POST /loans

Request

POST /loans
Headers:
  x-organization-id: {organizationId}
  x-idempotency-key: {unique-key}
Body:
{
  "organizationUserId": "orguser-123",
  "principalAmount": 500000,
  "period": 12,
  "interestRate": 10,
  "interestRateType": "YEARLY",
  "interestCalculationType": "REDUCING_BALANCE",
  "interestPaymentTiming": "WITH_INSTALLMENTS",
  "installmentType": "EQUAL_TOTAL",
  "paymentAllocationOrder": "INTEREST_FIRST",
  "startDate": "2026-06-01",
  "purpose": "Business expansion",
  "bankAccountId": "bank-456",
  "loanSecurities": [
    {
      "type": "GUARANTOR",
      "organizationUserId": "orguser-789",
      "description": "John Doe - Co-signer"
    },
    {
      "type": "COLLATERAL",
      "value": 600000,
      "description": "Motorcycle - 2020 model"
    }
  ],
  "disbursementFeePercentage": 1.0,
  "disbursementFeeType": "PERCENTAGE"
}
Required fields:
FieldTypeDescription
organizationUserIdstringBorrower (must have “member” role, be active, no existing ACTIVE/DEFAULTED loans)
principalAmountnumberLoan amount
periodnumberLoan term in months
interestRatenumberInterest rate percentage
interestRateTypeenum”MONTHLY” or “YEARLY”
interestCalculationTypeenum”SIMPLE”, “COMPOUND”, or “REDUCING_BALANCE”
startDatestringLoan start date (ISO format)
bankAccountIdstringBank account for disbursement
Optional fields:
FieldTypeDefaultDescription
interestPaymentTimingenumFrom org settings”IN_ADVANCE” or “WITH_INSTALLMENTS”
installmentTypeenumFrom org settings”EQUAL_PRINCIPAL” or “EQUAL_TOTAL”
paymentAllocationOrderenumFrom org settings”INTEREST_FIRST”, “PRINCIPAL_FIRST”, or “PROPORTIONAL”
purposestringnullLoan purpose description
loanSecuritiesarray[]Guarantors, collateral, savings requirements
disbursementFeeTypeenumFrom org settings”PERCENTAGE” or “FIXED_AMOUNT”
disbursementFeePercentagenumberFrom org settingsFee percentage (e.g., 1.0 = 1%)
disbursementFeeFixedAmountnumberFrom org settingsFixed fee amount
bankChargeAmountnumbernullBank transfer charges
skipNegativeBalanceCheckbooleanfalseAllow negative balances (use with caution)

What Happens Automatically

When loan is created, the system performs these actions atomically in one database transaction:
1

Validate member eligibility

  • Member exists in organization
  • Member is active
  • Member has “member” role assigned
  • Member has no existing ACTIVE or DEFAULTED loans
Error if: Member ineligible or already has active loan
2

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
Error if: Violates organization loan policies
3

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
Error if: Insufficient securities
4

Calculate installments

  • Computes EMI (Equated Monthly Installment)
  • Generates installment schedule
  • Calculates total interest
  • Applies configured rounding rules
Installments created with due dates based on period
5

Create loan record

  • Status set to ACTIVE (not PENDING)
  • Loan assigned unique account number
  • Installment schedule locked
  • Securities stored as JSON
Result: Loan immediately active and disbursed
6

Create ledger accounts

  • LOAN_RECEIVABLE account (principal tracking)
  • INTEREST_RECEIVABLE account (interest tracking)
  • PENALTY_RECEIVABLE account (penalty tracking)
Result: Dedicated ledger accounts for this loan
7

Post disbursement journal entry

Basic entry (interest with installments, fee from cash):
Dr LOAN_RECEIVABLE           500,000
Dr INTEREST_RECEIVABLE       total_interest
Cr CASH (bank account)       500,000 - fee
Cr INTEREST_INCOME           total_interest
Cr DISBURSEMENT_FEE_INCOME   fee_amount
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
Result: Loan disbursed, accounts updated
8

Send notification

If organization has email/SMS enabled:
  • Email sent to borrower with loan details
  • SMS sent with loan approval message
  • Message includes installment amount and schedule
Message example: “Your loan of 500,000 RWF has been approved! 12 monthly installments of 45,000 RWF each.”

Response

Success (201 Created):
{
  "message": "Loan created successfully",
  "data": {
    "id": "loan-123",
    "accountNumber": "L-00042",
    "organizationUserId": "orguser-123",
    "principalAmount": 500000,
    "period": 12,
    "interestRate": 10,
    "interestRateType": "YEARLY",
    "status": "ACTIVE",
    "startDate": "2026-06-01T00:00:00.000Z",
    "disbursementFee": 5000,
    "loanReceivableLedgerAccountId": "acc-456",
    "interestReceivableLedgerAccountId": "acc-457",
    "penaltyReceivableLedgerAccountId": "acc-458",
    "createdAt": "2026-06-13T10:30:00.000Z"
  }
}

Errors

StatusErrorCause
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
400Various validation errorsInvalid data, decimal precision issues, etc.

Stage 3: Post-Disbursement

Who: Loan Officer, Treasurer

Immediate Actions

1

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
Method 2: Bank transfer:
  • Transfer from organization bank account to member’s account
  • Record bank reference number
  • Amount: Principal - (fees/interest deducted from cash)
Method 3: Cash from office:
  • Withdraw from organization bank
  • Give cash to member
  • Member signs receipt
2

File loan documentation

  • Signed loan agreement
  • Collateral documents (titles, appraisals, photos)
  • Guarantor agreements
  • Disbursement receipt
  • Security documents
3

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
4

Set up payment reminders

  • Add loan to monitoring list
  • Set up automatic payment reminders (3-5 days before due date)
  • Note first payment due date
  • Configure penalty rules if applicable

Ongoing Monitoring

1

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)
2

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
3

Monitor loan health

  • Review arrears regularly (Loans Outstanding report)
  • Track days overdue
  • Classify loans (current, 1-30 days, 31-90 days, 90+ days)
  • Update status to DEFAULTED if necessary

Disbursement Fee & Interest Payment Options

Fee Deduction Options

Option 1: Fee deducted from cash (default):
Dr LOAN_RECEIVABLE           500,000
Cr CASH                      495,000  (500,000 - 5,000 fee)
Cr DISBURSEMENT_FEE_INCOME     5,000
Member receives: 495,000 RWF Option 2: Fee deducted from savings (if disbursementFeeFromSavings: true):
Main entry:
Dr LOAN_RECEIVABLE           500,000
Cr CASH                      500,000

Separate fee entry:
Dr SAVINGS (member)            5,000
Cr DISBURSEMENT_FEE_INCOME     5,000
Member receives: 500,000 RWF in cash, but 5,000 deducted from savings

Interest Payment Timing Options

Option 1: Interest paid with installments (default):
Dr LOAN_RECEIVABLE            500,000
Dr INTEREST_RECEIVABLE         60,000
Cr CASH                       500,000
Cr INTEREST_INCOME             60,000
Member pays interest gradually with each installment. Option 2: Interest paid in advance from cash (if interestPaymentTiming: "IN_ADVANCE"):
Dr LOAN_RECEIVABLE            500,000
Cr CASH                       440,000  (500,000 - 60,000 interest)
Cr INTEREST_INCOME             60,000
Member receives: 440,000 RWF (interest deducted upfront) Option 3: Interest paid in advance from savings (if interestPaymentTiming: "IN_ADVANCE" and advanceInterestFromSavings: true):
Main entry:
Dr LOAN_RECEIVABLE            500,000
Cr CASH                       500,000

Advance interest entry:
Dr SAVINGS (member)            60,000
Cr INTEREST_RECEIVABLE         60,000
Member receives: 500,000 RWF in cash, but 60,000 deducted from savings

Configuration from Organization Settings

Several loan parameters default to organization-wide settings (from loanSettings field):
SettingDefaultDescription
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_percentage0Default fee percentage
disbursement_fee_type”PERCENTAGE""PERCENTAGE” or “FIXED_AMOUNT”
disbursement_fee_fixed_amount0Fixed fee amount
deduct_disbursement_fee_from_savingsfalseDeduct fee from savings instead of cash
deduct_advance_interest_from_savingsfalseDeduct advance interest from savings instead of cash
These settings apply automatically unless overridden in the loan creation request.

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-key header 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

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
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
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
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
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
If already days/weeks later:
  • Member must repay according to schedule
  • Or negotiate restructuring with loan officer
  • Cannot delete disbursed loan (funds already given)
Problem: Defaults from organization settings not being usedCheck:
  • Are organization loanSettings configured?
  • Do request body fields override settings?
  • Verify settings with GET /organizations/
Note: Request body parameters take precedence over organization defaults

Future Enhancement: Multi-Stage Workflow

PENDING status exists in the database but is not currently used. A future version may implement:
  1. POST /loans with status=PENDING (draft loan application)
  2. PUT /loans//approve endpoint to change PENDING → ACTIVE
  3. DELETE /loans/ to cancel PENDING loans
  4. Workflow states and approval process
This would enable:
  • Loan applications submitted for review
  • Committee approval before disbursement
  • Separation of application and disbursement
  • Cancellation of unapproved loans
Currently: All loans created via API are immediately ACTIVE and disbursed.

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