Servicing actions
A governed catalog of balance, schedule, interest, charge, refund, restriction, and terminal actions—native ledger commands and LendEasy capabilities under one case, maker-checker, and governance-record contract.
Servicing actions change authoritative loan state. They come in two categories:
- LendEasy-native capabilities have their own APIs with their own lifecycles and approval rules: payments, fundings, autopay, statements, and restrictions.
- Everything the ledger already knows how to do is a governed native command. The command is selected with a
commandquery parameter on the relevant route—most oftenPOST /v1/loans/{loanId}/transactions?command=…—and executes immediately unless maker-checker parks it for a second approver.
Both categories run under the same governance contract: case context on human calls, per-command permissions, and an append-only governance ledger.
The governance contract
Every human servicing mutation carries a LendEasy-Case header naming an open case for the borrower when case management is enabled. A human call without it is rejected before anything executes. Automation never sends the header; its context is recorded from the triggering system instead.
curl -X POST "$BASE/v1/loans/7204/transactions?command=waiveinterest" \
-H "Authorization: Bearer $TOKEN" \
-H "LendEasy-Tenant: demo-lender" \
-H "LendEasy-Case: case_CEDAR_HARDSHIP_4" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: cedar-waive-2027-08-20" \
-d '{
"transactionDate": "2027-08-20",
"transactionAmount": 92.75,
"note": "Hardship interest waiver per case plan.",
"dateFormat": "yyyy-MM-dd",
"locale": "en"
}'
Command bodies are native: typically transactionDate, an optional transactionAmount, an optional note, and the dateFormat/locale envelope. The response is a command envelope; read the loan afterward for canonical state.
{
"officeId": 1,
"clientId": 412,
"loanId": 7204,
"resourceId": 30514,
"changes": {
"transactionDate": "2027-08-20",
"transactionAmount": 92.75
}
}
There is no preview endpoint and no preview token. Decide from authoritative reads—GET /v1/loans/{loanId}?associations=repaymentSchedule,transactions and GET /v1/loans/{loanId}/summary—and from the evidence attached to the case.
Maker-checker: the second person
Second-person control is native maker-checker. A command flagged for checking does not execute; it parks, and the caller’s response identifies the parked audit entry. A different user holding the command’s checker permission then decides:
curl "$BASE/v1/makercheckers" \
-H "Authorization: Bearer $TOKEN" \
-H "LendEasy-Tenant: demo-lender"
[
{
"id": 118,
"actionName": "WRITEOFF",
"entityName": "LOAN",
"resourceId": 7204,
"maker": "ana.alvarez",
"madeOnDate": "2027-08-20T17:14:09Z",
"commandAsJson": "{\"transactionDate\":\"2027-08-20\"}"
}
]
curl -X POST "$BASE/v1/makercheckers/118?command=approve" \
-H "Authorization: Bearer $TOKEN" \
-H "LendEasy-Tenant: demo-lender"
Approval executes the original command exactly as the maker submitted it; rejection discards it. The maker can never check their own command. Reschedules do not use this inbox: they carry their own request→approve workflow where the approver must differ from the requester.
Governance records
Every execution writes a governance record—who acted, in which context, through which route:
curl "$BASE/v1/loans/7204/governance-records" \
-H "Authorization: Bearer $TOKEN" \
-H "LendEasy-Tenant: demo-lender"
{
"items": [
{
"governanceId": 902,
"occurredAt": "2027-08-20T17:14:09Z",
"contextType": "CASE",
"contextRef": "case_CEDAR_HARDSHIP_4",
"permissionCode": "WAIVEINTERESTPORTION_LOAN",
"route": "POST /v1/loans/7204/transactions?command=waiveinterest",
"actorClass": "HUMAN",
"correlationId": "corr_01JZ7M5KX2"
}
]
}
contextType distinguishes DIRECT, CASE, CASE_AUTOMATION, and SYSTEM_TRIGGER executions. A customer-scoped view exists at GET /v1/customers/{customerId}/governance-records.
Catalog
Schedule and interest
| Action | Mechanism | Undo |
|---|---|---|
| Re-amortize | POST /v1/loans/{loanId}/transactions?command=reAmortize |
command=undoReAmortize |
| Re-age | POST /v1/loans/{loanId}/transactions?command=reAge |
command=undoReAge |
| Reschedule | POST /v1/rescheduleloans + POST /v1/rescheduleloans/{scheduleId}?command=approve|reject |
Corrective reschedule |
| Waive accrued interest | POST /v1/loans/{loanId}/transactions?command=waiveinterest |
— |
| Waive an interest payment | POST /v1/loans/{loanId}/transactions?command=interestPaymentWaiver |
— |
| Pause interest accrual | POST/PUT/DELETE /v1/loans/{loanId}/interest-pauses |
Update or delete the pause |
| SCRA/MLA protection | SCRA_MLA_PROTECTED restriction today; computed rate caps planned |
Clear the restriction |
Charges
| Action | Mechanism |
|---|---|
| Add a charge | POST /v1/loans/{loanId}/charges |
| Adjust a charge | POST /v1/loans/{loanId}/charges/{loanChargeId}?command=adjustment (or PUT before financial dependencies) |
| Waive a charge | POST /v1/loans/{loanId}/charges/{loanChargeId}?command=waive |
| Refund a charge | POST /v1/loans/{loanId}/transactions?command=chargeRefund |
Payments, credits, and refunds
| Action | Mechanism |
|---|---|
| Reverse a payment | No command—the provider-driven payment return flow |
| Record a recovery payment | POST /v1/loans/{loanId}/transactions?command=recoverypayment |
| Issue a cash refund | POST /v1/loans/{loanId}/transactions?command=refundByCash |
| Issue a goodwill credit | POST /v1/loans/{loanId}/transactions?command=goodwillCredit |
| Record a merchant refund | POST /v1/loans/{loanId}/transactions?command=merchantIssuedRefund |
| Refund a payout | POST /v1/loans/{loanId}/transactions?command=payoutRefund |
| Refund a credit balance | command=creditBalanceRefund, cash out via a REFUND funding |
Accounting, restrictions, and terminal state
| Action | Mechanism | Undo |
|---|---|---|
| Write off a balance | POST /v1/loans/{loanId}/transactions?command=writeoff |
command=undowriteoff |
| Charge off a loan | POST /v1/loans/{loanId}/transactions?command=charge-off |
command=undo-charge-off |
| Flag suspected fraud | PUT /v1/loans/{loanId}?command=markAsFraud |
Same route with fraud: false |
| Apply a servicing restriction | POST /v1/customers/{customerId}/restrictions |
POST …/restrictions/{restrictionId}/clear |
| Terminate a contract | POST /v1/loans/{loanId}?command=contractTermination |
command=undoContractTermination |
| Close a loan | POST /v1/loans/{loanId}/transactions?command=close |
— |
The ledger’s command set also includes foreclosure, but the public product catalog covers unsecured consumer installment lending, BNPL, and merchant advances, not real-property servicing, so it carries no action page. Repayment and disbursement commands are internal-only—money always moves through the payment and funding APIs.
Undo is specific, never generic
There is no generic undo route. Undo exists only where a dedicated command exists: undowriteoff, undo-charge-off, undoReAge, undoReAmortize, and undoContractTermination. Each undo is itself a governed command with its own permission, its own governance record, and its own maker-checker configuration. Anything else is corrected by a new compensating action; nothing deletes history.
Choosing among schedule actions
| Borrower outcome | Choose | Reason |
|---|---|---|
| Keep existing remaining due dates, spread overdue amounts across them | Re-amortize | It preserves the schedule horizon. |
| Set a new first due date, cadence, and number of installments | Re-age | It explicitly constructs a replacement remaining schedule. |
| Change broader contract terms under a request-and-approve workflow | Reschedule | It supports a governed modification proposal with a separate approver. |
| Forgive interest without changing the principal schedule | Interest waiver | It records the forgiven economic component directly. |
Never choose based only on a friendlier payment amount. Compare total cost, maturity, disclosures, delinquency treatment, and downstream autopay/statement consequences before executing—the schedule read and loan summary give you the facts, and the case holds the comparison you relied on.