Appearance
Delinquency Bucketing
Delinquency bucketing is Fineract's mechanism for classifying overdue loans into named risk tiers based on how many days past due they are. Buckets drive portfolio risk reporting (PAR metrics), automated charge triggers, and business event signals for collections workflows.
What Is a Delinquency Bucket?
A delinquency bucket is a named range of days-past-due (DPD). Common examples:
| Bucket name | DPD range | Typical PAR metric |
|---|---|---|
| Current | 0 days | Not delinquent |
| 1-30 days | 1 - 30 | PAR 1 |
| 31-60 days | 31 - 60 | PAR 30 |
| 61-90 days | 61 - 90 | PAR 60 |
| 91-180 days | 91 - 180 | PAR 90 |
| 180+ days | 181+ | PAR 180 / Write-off risk |
The ranges, names, and number of buckets are fully configurable. An institution might define six buckets or twenty - depending on their portfolio management and regulatory reporting requirements.
How Bucket Classification Works
Classification runs automatically during COB (Close of Business). Each night, Fineract:
- Calculates the number of days past due for every active overdue loan
- Compares DPD against all configured bucket ranges
- Assigns the loan to the highest matching bucket
- Records the classification in the loan's
delinquency_rangefield - Emits a
LoanDelinquencyRangeChangeBusinessEventbusiness event if the bucket changed since the last run
Bucket assignment is updated every night. A loan that moves from the 1-30 bucket to the 31-60 bucket (because it remained unpaid for another month) is reclassified automatically on the COB run that crosses the threshold.
Creating a Delinquency Bucket Configuration
Step 1: Create Individual Ranges
bash
POST /fineract-provider/api/v1/delinquency/ranges
Authorization: Basic <base64>
Fineract-Platform-TenantId: default
Content-Type: application/jsonjson
{
"classification": "1 to 30 days",
"minimumAgeDays": 1,
"maximumAgeDays": 30,
"locale": "en"
}Create one range per bucket tier. For the highest tier (open-ended), omit maximumAgeDays or set it to a very large number:
json
{
"classification": "181+ days",
"minimumAgeDays": 181,
"locale": "en"
}Step 2: Create a Delinquency Bucket
A bucket is a named collection of ranges:
bash
POST /fineract-provider/api/v1/delinquency/bucketsjson
{
"name": "Standard Delinquency Bucket",
"ranges": [1, 2, 3, 4, 5]
}The ranges array contains the IDs of the ranges created in Step 1.
Step 3: Assign the Bucket to a Loan Product
bash
PUT /fineract-provider/api/v1/loanproducts/{productId}json
{
"delinquencyBucketId": 1
}All loans created from this product will be classified using the assigned bucket.
Reading Delinquency Classification
On a single loan
bash
GET /fineract-provider/api/v1/loans/{loanId}/delinquencyResponse includes:
json
{
"delinquentDate": "2025-01-15",
"delinquentDays": 52,
"delinquentAmount": 1250.00,
"installmentLevelDelinquency": [
{
"rangeId": 2,
"classification": "31 to 60 days",
"delinquentAmount": 1250.00
}
]
}Installment-level vs Loan-level delinquency
Fineract supports delinquency tracking at two levels:
- Loan-level - the bucket is determined by the oldest unpaid installment's DPD
- Installment-level - each overdue installment is independently classified into a bucket, allowing a loan to span multiple buckets if it has installments overdue by different amounts of time
Installment-level delinquency gives more granular data for collections prioritisation. Enable it on the loan product with:
json
{
"enableInstallmentLevelDelinquency": true
}Listing All Delinquent Loans
Use the search API to filter loans by delinquency range:
bash
GET /fineract-provider/api/v1/loans/search?delinquencyRangeId=2For portfolio-level reporting, use the Fineract reporting module or query the database directly:
sql
-- Loans by delinquency bucket
SELECT
dr.classification AS bucket,
COUNT(l.id) AS loan_count,
SUM(ls.principal_outstanding_derived) AS outstanding_principal
FROM m_loan l
JOIN m_delinquency_range dr ON l.loan_delinquency_range_id = dr.id
JOIN m_loan_repayment_schedule ls ON ls.loan_id = l.id
WHERE l.loan_status_id = 300 -- Active loans
GROUP BY dr.classification
ORDER BY dr.minimum_age_days;PAR (Portfolio at Risk) Calculation
PAR X is the percentage of the outstanding portfolio principal where at least one installment is X or more days past due.
sql
-- PAR 30: loans with 30+ DPD as % of total active portfolio
SELECT
(
SELECT SUM(principal_outstanding_derived)
FROM m_loan
WHERE delinquent_days >= 30
AND loan_status_id = 300
) /
(
SELECT SUM(principal_outstanding_derived)
FROM m_loan
WHERE loan_status_id = 300
) * 100 AS par30_percentage;PAR values are always calculated against Business Date (not system date) to stay consistent with Fineract's own view of overdue status.
Business Events for Collections Workflows
Every time a loan moves to a new delinquency bucket, Fineract emits:
LoanDelinquencyRangeChangeBusinessEventThis event contains the loan ID, the previous bucket, the new bucket, and the current DPD. Subscribe to this event via Kafka or JMS to:
- Trigger escalation in your collections system
- Send automated SMS or push notifications to borrowers
- Assign the loan to a specialist collections officer
- Update risk scoring in your CRM
See the Business Events guide for connector setup.
Example event payload
json
{
"businessEventType": "LoanDelinquencyRangeChangeBusinessEvent",
"loanId": 1234,
"loanExternalId": "LOAN-ABC-001",
"delinquencyRange": {
"id": 3,
"classification": "61 to 90 days"
},
"delinquentDays": 67,
"delinquentDate": "2025-01-15"
}Viewing Configured Buckets and Ranges
bash
# All configured ranges
GET /fineract-provider/api/v1/delinquency/ranges
# All configured buckets
GET /fineract-provider/api/v1/delinquency/buckets
# A specific bucket with its ranges
GET /fineract-provider/api/v1/delinquency/buckets/{bucketId}Best Practices
- Align bucket boundaries with regulatory requirements - many central banks require specific PAR 30/60/90 reporting, so match your bucket ranges to those thresholds
- Use installment-level delinquency for complex products - especially for progressive or multi-tranche loans where individual installments may have very different overdue ages
- Subscribe to
LoanDelinquencyRangeChangeBusinessEventfor real-time collections triggers rather than polling the API nightly - Re-evaluate bucket configuration after portfolio growth - a single bucket covering 1-90 days is fine for small portfolios but obscures risk concentration as volumes grow