Credit API — endpoints, permissions, shapes (Task 10.2)
Status: ✅ Complete
Plan reference: Task 10.2 in the Credit module implementation plan (operations workspace root)
All credit endpoints are namespaced under /credit. They cover facility CRUD + lifecycle transitions, payments, configuration, and reporting. For the underlying tables see credit data model; for the math behind :ref/schedule see credit engine.
Permissions
Every endpoint is gated by one of these permissions:
| Permission | Scope |
|---|---|
credit:view | Read facilities, schedules, payments, reports |
credit:create | Create a facility and submit for review |
credit:review | Maker-checker review (approve to Shariah / reject) |
credit:approve | Shariah/credit approval, mark asset owned, activate, non-accrual |
credit:record_payment | Record payments and early settlements |
credit:config_manage | Manage the posting account-map |
Facilities — CRUD + lifecycle
| Method + path | Permission | Purpose |
|---|---|---|
POST /credit/facilities | credit:create | Create a draft facility |
GET /credit/facilities | credit:view | List / filter facilities |
GET /credit/facilities/:ref | credit:view | Fetch one facility by CF-YYYYMMDD-XXXX ref |
GET /credit/facilities/:ref/schedule | credit:view | The generated repayment schedule |
POST /credit/facilities/:ref/review | credit:create | Submit draft → pending_review |
POST /credit/facilities/:ref/approve | credit:review / credit:approve | Advance review → shariah_review → approved |
POST /credit/facilities/:ref/reject | credit:review | pending_review/shariah_review → rejected |
POST /credit/facilities/:ref/asset-owned | credit:approve | Mark the asset acquired/owned |
POST /credit/facilities/:ref/activate | credit:approve | VR-001 gate + atomic origination journal → active |
POST /credit/facilities — create
// request
{
"customerId": "cus_123",
"productType": "murabaha", // | "ijarah" | "diminishing_musharakah"
"assetCost": "100000000.0000",
"downPayment": "0.0000",
"rate": 8, // annual %, used by DM/Ijarah engine
"termPeriods": 15,
"periodsPerYear": 1,
"fixedTotalProfit": "20000000.0000", // REQUIRED for murabaha; omit for DM/ijarah
"asset": { "description": "Toyota Hilux fleet", "cost": "100000000.0000" }
}
// response
{
"facilityRef": "CF-20260615-0042",
"status": "draft",
"facilityAmount": "100000000.0000"
}
Validation rejects a Murabaha facility with no fixedTotalProfit, and rejects DM/Ijarah facilities that supply one (their profit is rate-derived).
GET /credit/facilities/:ref/schedule
Returns the per-period plan from credit_schedule_lines:
{
"facilityRef": "CF-20260615-0042",
"lines": [
{
"period": 1,
"paymentDate": "2027-06-15",
"beginningBalance": "100000000.0000",
"principalPortion": "6666666.6700",
"profitPortion": "8000000.0000",
"totalPayment": "14666666.6700",
"endingBalance": "93333333.3300",
"ethicaOwnershipPct": "93.33",
"customerOwnershipPct": "6.67",
"status": "pending"
}
// ... period 2..15
]
}
POST /credit/facilities/:ref/activate
Enforces VR-001 (asset must be owned_by_ethica for Murabaha/Ijarah or co_owned for DM) and atomically posts the origination journal. On a failed gate it returns 409 Conflict; on an unmapped account the posting fails closed.
// response
{ "status": "active", "originationJournalId": "je_88f1", "activatedAt": "2026-06-15T10:00:00Z" }
Payments + servicing
| Method + path | Permission | Purpose |
|---|---|---|
POST /credit/facilities/:ref/payments | credit:record_payment | Record a payment (incl. partial / extra / early settlement) |
GET /credit/facilities/:ref/payments | credit:view | List payments on a facility |
POST /credit/facilities/:ref/overdue | credit:approve | Trigger overdue evaluation (also run by the daily job) |
POST /credit/facilities/:ref/non-accrual | credit:approve | Flag the facility non-accrual |
POST /credit/facilities/:ref/payments
// request
{
"amount": "14666666.6700",
"paymentDate": "2027-06-15",
"reference": "TRX-99812",
"earlySettlement": false // when true: DM/Ijarah waive future rent;
// Murabaha recognises all remaining deferred profit
}
// response
{
"paymentId": "pay_55",
"journalEntryId": "je_91c2",
"appliedTo": [{ "period": 1, "status": "paid", "paidAmount": "14666666.6700" }],
"extraPayment": false
}
While a facility is non-accrual, the payment posts Dr cash / Cr principal only — no income is recognised.
Config — posting account-map
| Method + path | Permission | Purpose |
|---|---|---|
GET /credit/config/account-map | credit:config_manage / credit:view | Read the product × event × side → GL map |
PUT /credit/config/account-map | credit:config_manage | Replace/update mappings |
// PUT request
{
"mappings": [
{ "productType": "murabaha", "eventType": "origination", "side": "debit", "glAccount": "1300" },
{ "productType": "murabaha", "eventType": "origination", "side": "credit", "glAccount": "1001" },
{ "productType": "murabaha", "eventType": "origination", "side": "credit", "glAccount": "2400" }
]
}
If a required mapping is missing at posting time the engine fails closed rather than defaulting an account.
Reports
| Method + path | Permission | Purpose |
|---|---|---|
GET /credit/reports/portfolio | credit:view | Outstanding by product / status |
GET /credit/reports/income | credit:view | Income contribution from the credit book |
GET /credit/reports/shariah-compliance | credit:view | Shari'ah-compliance summary |
Files
- Controller —
ethica-api/src/credit/credit.controller.ts - Service —
ethica-api/src/credit/credit.service.ts - DTOs —
ethica-api/src/credit/dto/*.dto.ts
Related
- Credit data model — the tables behind these endpoints.
- Credit engine — how
:ref/scheduleis computed. - Credit operations guide — the staff-facing flow.