Case effects
A case effect is the workflow's own committed operation — callers can read one and hand a terminal failure back to the executor, but nobody can issue one.
A workflow can carry out an account change itself rather than raising a task and waiting for somebody to do it: cancel autopay, release a protective hold, refer a protected case, open a legal action. Deciding and doing are otherwise two separate events, and the gap between them is where money keeps quietly moving. A case effect closes that gap: the workflow records a specific commitment it will hold — cancel autopay, apply a restriction, open follow-up work — the executor performs it, and the case cannot close while one is still owed.
A caller may
- List the effects on a case, or read one with its attempts and its result
- Return a terminal FAILED effect to PENDING
Only the workflow may
- Decide that an effect is owed, and name its effectType
- Say what the executor performs
The routes
| Route | Purpose | Permission |
|---|---|---|
GET /v1/servicing/cases/{caseId}/effects |
List effects on the case. | READ_SERVICING_CASE |
GET /v1/servicing/cases/{caseId}/effects/{effectRef} |
Read one effect with attempts and result. | READ_SERVICING_CASE |
POST /v1/servicing/cases/{caseId}/effects/{effectRef}?command=retry |
Return a terminal FAILED effect to PENDING. |
RETRY_SERVICING_CASE_EFFECT |
effectType comes from a fixed list the workflow definition owns — the governed operations boundary, expressed for automation.
The one cursor-paged list in servicing
curl "$BASE/v1/servicing/cases/1001/effects?status=FAILED&limit=50" \
-H "Authorization: Bearer $TOKEN" \
-H "LendEasy-Tenant: demo-lender"
{
"items": [
{
"effectRef": "effect-cancel-autopay-1001-1",
"caseId": 1001,
"effectType": "CANCEL_AUTOPAY",
"triggerType": "LMS_EVENT",
"status": "COMPLETED",
"attemptCount": 1,
"createdAt": "2027-05-05T18:30:00Z",
"completedAt": "2027-05-05T18:30:02Z"
}
],
"nextCursor": null,
"hasMore": false
}
status and effectType are repeatable — send each value again to include several states or types in one read. triggerType, createdFrom, and createdTo take a single value.
This collection is the one current cursor-paged servicing list: it takes limit (1–200, default 50) plus an opaque newest-first cursor and returns nextCursor, while most servicing collections use limit/offset. Send nextCursor back unchanged and stop when it is null. offset and cursor are not interchangeable here — effects arrive at the head of a newest-first list while you are walking it, and counting from the front skips exactly the rows a paging client was reading for. See Pagination.
effectRef is a stable string, not a numeric ID — the identifier both the point read and the retry command take. The point read adds the trigger, attempt history, timestamps, and redacted result or failure references — enough to explain what happened without exposing provider-native payloads.
Retry is recovery, not re-issue
Retry exists for one situation: an effect reached terminal FAILED and the underlying cause has since been resolved.
curl -X POST "$BASE/v1/servicing/cases/1001/effects/effect-cancel-autopay-1001-1?command=retry" \
-H "Authorization: Bearer $TOKEN" \
-H "LendEasy-Tenant: demo-lender" \
-H "Content-Type: application/json" \
-d '{ "reason": "The transient downstream failure has been resolved." }'
{
"effectRef": "effect-cancel-autopay-1001-1",
"status": "PENDING",
"resourceId": 1001
}
Three properties matter:
command=retryis required. The route accepts no other command, so the verb cannot be widened later by a client guessing at a parameter.reasonis required. A retry is a recorded operational decision, not a refresh button.- The effect returns to
PENDINGand the workflow executor performs the same operation. The retry does not carry a new payload, so it cannot become a way to change what the workflow decided to do.
An effect that is not terminally failed cannot be retried, and a retry never duplicates an effect a provider already accepted. Where an external side effect may already exist, it follows its own cancellation and reconciliation path instead.
A failed effect is not a failed task
Effects, action requests, and tasks answer three different questions on the same case, and conflating them hides work:
| Record | Means |
|---|---|
| Task | A worker — human or AI — owes the next unit of work. |
| Action request | An operation was opened for approval, deferred, manual, external, or unavailable handling. |
| Case effect | The workflow itself committed to a specific operation and the executor owns it. |
Closing the task does not clear the effect: a matter cannot be closed while the workflow still owes a committed operation. Closing over a failed CANCEL_AUTOPAY would leave the borrower’s debit running behind a case everyone has agreed is finished.
COMPLETED is a statement about the request, not about the loan: the operation was executed and acknowledged. Confirm the resulting financial or account state through the owning Lending Core resource, which is the record a borrower’s money actually lives in.