Servicing reports
Read bounded workforce, collections, interaction, compliance, AI-quality, and drift projections with drill-through to the base collections, plus the curated investor reporting pack.
Servicing reports are bounded projections, not an analytics warehouse. Each one answers a specific operating question, echoes the query it actually applied, and points back at the canonical collection so a number can always be opened as work.
The six operational reports
| Report | Route | Answers |
|---|---|---|
| Workforce | GET /v1/servicing/reports/workforce |
Where is work piling up, and who has capacity? |
| Collections | GET /v1/servicing/reports/collections |
Which delinquent matters are contactable, and when? |
| Interaction exceptions | GET /v1/servicing/reports/interactions/exceptions |
Which channel work was missed, unmatched, or failed? |
| Compliance | GET /v1/servicing/reports/compliance |
What did policy decide, under which rules and regimes? |
| AI quality | GET /v1/servicing/reports/ai-quality |
How grounded and how reviewed is AI output? |
| Reconciliation | GET /v1/servicing/reports/reconciliation |
Where has servicing state drifted from the LMS? |
| Promise & allocation | GET /v1/servicing/reports/promise-allocation |
Are promises being kept, and is work reaching the people who can keep them? |
All six take READ_SERVICING_REPORTS, accept exact structured filters plus limit/offset and sort/order, and return the same outer shape:
{
"asOf": "2027-05-05T18:30:00Z",
"effectiveQuery": { "limit": 50, "offset": 0 },
"rows": [],
"returnedCount": 0,
"hasMore": false
}
effectiveQuery echoes what the server actually applied after defaults and caps. Read it before trusting a total — a report page is a bounded slice, and returnedCount counts the rows in this response, not the population behind it.
Drill-through, not a dead end
The workforce report aggregates by QUEUE or WORKER and gives each row a canonical link back into the base collection:
{
"dimension": "QUEUE",
"summary": {
"openTaskCount": 18,
"activeTaskCount": 7,
"staleAcceptedTaskCount": 1,
"overdueTaskCount": 2,
"slaBreachedTaskCount": 1,
"openCaseCount": 42,
"availableWorkerCapacity": 21
},
"rows": [
{
"queueCode": "COLLECTIONS",
"openTaskCount": 18,
"overdueTaskCount": 2,
"oldestTaskAgeMinutes": 91,
"eligibleWorkerCapacity": 28,
"availableWorkerCount": 4,
"drillThrough": "/v1/servicing/tasks?queueCode=COLLECTIONS"
}
]
}
drillThrough is the report’s contract with the workspace: the aggregate and the list it opens are the same query. staleAccepted=true isolates accepted work that has stopped moving — the case a throughput average hides.
Collections and contact timing
The collections report joins current LMS financial facts to servicing work state, so a row carries both the money and the permission to act on it:
{
"caseId": 1001,
"customerRef": "123",
"loanRef": "456",
"priority": "HIGH",
"promiseState": "ACTIVE",
"financialFactsSource": "LMS",
"daysOverdue": 22,
"delinquencyBucket": "DPD_1_29",
"overdueAmount": 517.14,
"currency": "USD",
"contactIntensity": 2,
"remainingContactCapacity": 5,
"lastContactAttemptAt": "2027-05-04T21:04:00Z",
"nextPermittedContactAt": "2027-05-06T15:00:00Z",
"protectiveStateActive": false,
"contactBlocked": false
}
financialFactsSource names where the balance came from; the servicing plane does not recompute delinquency. nextPermittedContactAt and remainingContactCapacity are planning aids from the contact budget, not authorizations — the owning operation still runs its own execution-time check immediately before contact, and can block work this report showed as permitted.
Compliance, AI quality, and drift
The compliance report exposes decision outcomes with their rule and regime detail, filterable by outcome, hardStop, regime, ruleCode, channel, and proposedAction:
{
"decisionId": 6117,
"proposedAction": "OUTBOUND_CONTACT",
"channel": "PHONE",
"outcome": "ALLOWED",
"hardStop": false,
"rules": [{ "ruleCode": "CONTACT_WINDOW_CA", "regime": "REG_F", "outcome": "ALLOWED" }]
}
The AI-quality report projects retained output, grounding, runtime, and review state together — session, worker and adapter codes, autonomyLevel, runtimeVersion, policyVersion, workPacketVersion, confidence, groundingStatus, and the reviewer and review time. Because runtime and policy versions are rows rather than metadata, a quality regression can be attributed to the version that caused it instead of being averaged away.
The reconciliation report returns current servicing-to-LMS drift with its classification, priority, and the review task opened for it:
{
"driftId": 81,
"driftType": "EXPECTED_STATE_MISMATCH",
"objectType": "AUTOPAY",
"objectRef": "456",
"customerRef": "123",
"status": "OPEN",
"priority": "HIGH",
"reviewTaskId": 2005,
"detectedAt": "2027-05-05T18:30:00Z"
}
This is the servicing-plane view of drift. Money-movement exceptions between provider events, intents, and the ledger stay in the Lending Core catalog — see Reconciliation & drift.
The investor reporting pack
Two routes serve a curated 18-report pack: GET /v1/servicing/reports/investor lists the catalog, and GET /v1/servicing/reports/investor/{reportKey} returns one report. Both accept READ_REPORT or READ_SERVICING_REPORTS.
Every response carries its provenance:
{
"provenance": {
"label": "Reporting snapshot",
"loadKey": "INVESTOR_V1:2027-05-05",
"sourceWatermark": "portfolio-v1:2027-05-05",
"generationMode": "SNAPSHOT_LOAD",
"nonAuthoritative": true
}
}
The pack is a snapshot load. It never reads live servicing or ledger tables, and nonAuthoritative: true is a contract, not a disclaimer — reconcile against the authoritative source before publishing a figure externally.
A report renders as headline metrics, insights, a trend, a breakdown, a supporting table, and a methodology note. Metrics declare their own format (number, percent, currency) and tone, and delta/deltaLabel keys are omitted when no comparison period was supplied rather than returned as zero. The reportKey must come from the catalog; an arbitrary key is rejected.
methodology before comparing two figures. In the collections pack, for example, promise kept-rate is count-based while dollar kept-rate is value-based, and promise dollars are never counted as collected until the cash is realized.