LendEasy/DocsLMS + Servicing·v1
Start integrating
GuidesServicing WorkspaceHow a case moves

How a case moves

A case is a question about a borrower that somebody has to answer. The engine has one job — make sure that at every moment, either the machine can move the case forward, or a named person has been asked to.

A case runs itself. At every moment the engine either moves it to the next stage or asks a named person to, and it never rests in a state where neither is true. Nothing starts by itself: the Lending Core reports something about a loan, that report is recorded as a signal, and the signal opens a case. From that moment the case belongs to a workflow: an ordered set of steps, written as configuration rather than code, that the case walks until it can be closed with an outcome.

The engine never runs on a timetable of its own. It runs when something happens. A new event on the loan, a completed task, a granted approval, a recorded decision — each one wakes the engine, which looks at where the case stands and asks whether it can move.

THE LENDING COREA returned payment. A credit balance. A settled or failed payout. A governed change.recorded aswakes the engine againTHE CASE ENGINESignalCaseWorkflow instancepinned to one versionStepwhere it stands nowClosedwith an outcomelast step satisfiedPEOPLEA queue, a person who picks it up,and the outcome they recordcreates a taskwakes the engine
The engine sits between the loan and the people. It turns what the loan says into work somebody owns, and turns what they did back into movement.

Five words, and nothing else to learn

Almost everything in the engine is one of these five things.

Term What it is
Case The question about the borrower. It has a type, a status, an owner, and eventually an outcome.
Definition The written flow for one kind of case. Configuration, not code, and versioned.
Step One position in the flow. The case is always standing in exactly one. A step says what work it wants, what has to be true to leave, and where to go next.
Task One unit of work a step asks for. An AI agent takes it and carries it to an outcome; a person takes it where the task type reserves the decision for one. Either way it ends with a recorded outcome, and the outcome is what the next step reads.
Fact Something true about the loan or borrower, read live from the Lending Core or computed by the compliance engine. Facts are what a step’s rules are written about.

One more, because it explains a lot of behaviour. A visit is one occupancy of one step. A case that reaches a step, leaves, and comes back later has made two visits. Rules a step writes about its own work mean the work of the current visit — so a review completed the first time round cannot answer for a review that was never done the second time.

A signal does not automatically make a case

Every case begins with a signal: a recorded statement that something happened. Most come from the Lending Core. Others come from a borrower’s email or call, from external monitoring, or from a person creating a case by hand.

The signal is checked against the world first, in a fixed order, and only the last branch creates something new. This is what stops one borrower’s missed payment from opening a fresh case every night.

A signal arrivesSeen this exact signal before?Recorded no-op.Delivered twice, acted on once.A live case already about this?Link the signal to that case.No second case for the same matter.Part of a spent collections episode?Link it there.The contact budget already spent still counts.A closed case this would reopen?Reopen it, under the system actor.A duplicate is never reopened; its primary is.Open a new case, pin its workflow, enter the first step.All in one transaction, and refused if the case would have nothing to do and nobody to do it.
The order matters. Only a signal that matches nothing creates a case, which is why the same delinquency reported every night does not produce a pile of cases.

The anti-orphan rule

A case is created and opened in the same command, and its workflow starts before the case is checked for accountability — because a step that needs a person creates that task in the same transaction. Then the engine enforces the anti-orphan rule: an open case must derive an accountable next step. Automation, a documented wait, a named owner, or a real task. A case that derives none of those is rejected outright rather than saved.

That is a strong claim and it is deliberate. A case with nothing on it is not a runtime problem to clean up later. It is a defect in the flow’s configuration, and the platform would rather refuse to create the case than hold a borrower’s matter that nobody will ever look at.

Which flow a case gets

Two inputs, a fixed order, and nothing else participates.

  1. The reason’s override. A case reason may name a workflow key. A borrower whose hardship is medical goes to the medical-leave flow; every other hardship disclosure goes to the affordability flow. The reason names the key, not a version, because versions turn over and the reason cares which flow it wants rather than which version happened to be current when it was written.
  2. The case type’s default. Otherwise the type’s configured default, and failing that the active definition named after the type itself.

A reason that needs its own shape gets its own definition, never its own case type. Once chosen, the case is pinned to that exact version for life.

A change to a flow only affects cases that start after it is activated. A case half-way through a journey should not suddenly be judged against rules that did not exist when it started.

What a step actually is

A step is not a screen and not a piece of code. It is five rules written next to each other, read in a fixed order. Once you can see this shape, the whole engine follows from it.

only on arrivalon every trigger, for as long as the case stands hereONE STEPENTRYMay the casecome in at all?entryCriteriaNo means exceptionwork, never a skipWORKWhat does thisstep ask for?autoCreateWhenautoExecuteWhenEach is conditionalEXITMay the caseleave yet?exitCriteriaUsually reads thisstep’s own workGATESEvidence andapprovalsgatesChecked after theexit, never beforeROUTEWhich stepcomes next?next[]First match wins,in written ordercase arrivesmoves onA task in a queuesomeone picks it up and decidesAn effect the platform holdsexecuted against the loan, with retries
Entry is the only part read once. Everything else, including what work the step wants, is re-read every time the engine wakes.

A sixth field is what an operator sees. A step that waits on the outside world declares a wait status, and the case wears it. That is why a case can read as patiently waiting on the Lending Core rather than as an orphan nobody is working.

Each piece of work a step asks for is conditional. Some conditions are already settled when the case arrives; some can only become true afterwards — a payout that fails, an exception a reviewer records, a request the Lending Core rejects. Both kinds are re-read on every trigger, and each piece of work is raised at most once per visit.

The rules a step is written in

Every rule is a small expression over a fixed list of sources. There is no scripting language, no arbitrary query, and no way for a flow author to write code. If a rule needs something the list below cannot express, it is added as a named condition, reviewed like any other code, and registered by key.

That constraint is the reason a flow can be edited by the business without a release, and the reason a definition can be checked by a machine before it is allowed to go live.

A rule can ask about Meaning
fact A value read live from the Lending Core: days overdue, the payout state, the autopay state. Facts used for a contact or money decision are re-read at the moment of execution, never served from a remembered value.
derivedFact A value the servicing side computes: remaining contact capacity, whether a cease restriction applies, which channels are allowed.
case Registered fields on the case itself — status, reason, priority, sensitivity, and a small catalog of code-owned flags. Never tenant-configured extras.
signal Normalised fields from the signal that triggered the case. Never raw provider JSON.
task A task type’s status or its recorded outcome. For a type this step asks for, only this visit’s instance counts.
decision A judgement a named person wrote down, from a closed list. Silence never satisfies a step.
governance Whether the Lending Core has confirmed a specific change was actually made. This is how a step requires that something happened rather than that somebody said it did.
evidence · approval Documents attached, and approvals granted.
actionRequest · caseEffect The outcome of a governed operation, and the status of an effect.
condition The escape hatch: a registered, reviewed condition referenced by a stable key. A definition naming an unregistered key is rejected when it is saved.

Rules combine with all and any, which nest. They are evaluated in written order and short-circuit, so ordering inside a rule is meaningful. Every rule resolves to one of three answers, not two, and the third is the whole reason the platform is safe to run against real loans.

TRUE

The condition holds. The case may move.

FALSE

The condition does not hold. The case stays where it is.

MISSING

The engine could not find out. It never guesses and never advances. It raises real work asking a person to establish the fact.

The engine will not advance a case on a fact it could not read. It will not skip a step silently, because a failed entry raises exception work instead. It will not let an approval be satisfied by anything other than an authorised person, and never by the preparer of the request. An AI agent closes the work a step asks for and records its outcome like any other worker. What it cannot do is grant an approval, waive required evidence, or override a compliance hard stop — those are reserved to a named person, and the engine refuses them from any agent.

What happens when something wakes it

The engine is woken by a case being created or changing status, a task completing or being released, a fact snapshot changing, an action request or effect reaching a result, evidence or an approval arriving, a scheduled sweep, or an operator asking it directly. Whatever the trigger, the same sequence runs against the step the case is standing in.

Each row can end the pass. If the case does move, the sequence starts over from the top, so a case can walk several steps on a single event.

Is the case suspended?stop. A withheld fact makes no work.Been at this step already this pass?record the loop as a defect in the flowand give the queue work naming itIs this a terminal step?resolve the outcome and closeRe-assert the declared wait.Take one reading of the world.Has anything overtakenthe whole flow?a reaction fires, the case jumps,and the sequence starts overDoes this step want work itdoes not yet have?raise it now, at most once per visit,then take a fresh readingIs the exit satisfied?no: stop, the step still owes workunknown: raise missing-fact workAre the required facts there?no: raise missing-fact work and stopAre the gates satisfied?no: wait for the evidence or approvalWhich route matches first?none: stop where it standsMove to the next stepand start the sequence overseveral steps in one trigger
Note the highlighted row: the engine asks what work the step is missing before it asks whether the step may leave. Judging the exit first would end the pass before the work the step itself asked for exists.
Before judging anything, the engine takes a single reading of the world and judges every rule in that pass against it. No rule reads live state half-way through, so the same reading always produces the same result — and every step change records which reading it was based on and what each rule answered.

Moving to the next step

Advancing is four things in a fixed order, and the third is the one people do not expect.

1. Movethe case now standsat the new step2. Close the visitthe old occupancy ends,reason recorded3. Withdraw its workanything that step was stillasking for is taken back4. Open a new visitand enter the new step:entry, then its workWhy step three exists.A step’s tasks are that step’s request: call this borrower, review these facts. Once the case has moved on, therequest is stale — and a stale request that stays claimable is how a protective hold fails to stop a call that wasalready prepared. The task keeps its history; only its claim on somebody’s attention is removed.
Leaving a step takes back what it asked for. Only that step's own work is withdrawn, never a task somebody raised by hand or work another step is still waiting on.

Two smaller rules matter when reasoning about a flow. Routes are tried in written order and the first match wins, so a general catch-all placed above a specific branch will swallow it. And a route whose condition is unknown does not advance the case; it raises fact work instead, because the engine will not guess which way to go.

When the flow is overtaken

Some facts make an entire flow moot rather than satisfying the step the case happens to be standing in. A borrower who pays in full has stopped being a collections matter, and which step the case had reached when the money arrived is not a reason to keep working it.

Those are written once for the whole flow as reactions: a condition, the steps it is armed during, and where the case goes. They are evaluated before the step’s own exit logic on every pass, and the first match wins.

ContactWaitCadenceEscalateClose“The borrower paid in full.”armed during the four steps beneath itgoes straight hereFiring cancels work nobody has taken, with its own audited reason. Work somebody is already holding resolves through its owner: a person mid-task is told, not overruled. Nothing is ever marked completed by a reaction, because nobody did it.
A reaction states something true of the whole flow once, instead of patching the same escape into every step it could interrupt.

Effects: work the platform runs itself

A step can also ask for an effect: work the platform runs itself against the Lending Core or the servicing world, as opposed to a task for a person. Effects are a fixed, code-owned list. A flow author picks one by name and picks its target from two choices. There is no URL, no request body, no header, no script, and no way to name an arbitrary identifier.

Effect What it does
CANCEL_AUTOPAY Stops the automatic payment on the loan the triggering signal names.
RELEASE_PROTECTIVE_STATE Lifts the contact blocks this case is holding, and only this case’s. Another open case protecting the same borrower keeps its own.
REFER_PROTECTIVE_CASE Opens the protective successor a case’s own state names, records the origination, applies the block and the authoritative hold, and suppresses outreach.
OPEN_LEGAL_ACTION_CASE Opens the legal matter as its own case.
Recordedkept, safe to retryAttemptedoutside the case’s own writeDoneWait and retry1, 5, then longerAfter five attempts: failed for good,and exactly one action review is raisedA pending, retrying or failed effectblocks the case from closing.
An effect is a promise the platform keeps. It is recorded before it is attempted, retried on its own schedule, and a terminal failure becomes one person's problem rather than a silent gap.

How a case ends

A flow’s last step is marked terminal. Reaching it does not close the case by itself. Three things happen in order.

  1. The outcome is chosen. The definition’s terminal-outcome rules are tried in order and the first match names the case’s outcome: cured, refunded, referred to hardship, arrangement failed, not eligible. This happens before anything that reads the outcome is checked.
  2. The guardrails are re-checked. No active tasks and no unresolved effects, plus whatever else the terminal step’s own exit demands. This is deliberately belt and braces: the case state machine enforces it too.
  3. The case resolves and closes, recorded under the system actor.

Ownership does not veto a close. Every judgement the closure depended on was already recorded by whoever made it, on the tasks and decisions carrying their names, and that attribution survives. There is deliberately no “finished, but sitting open because it has an owner” state. Where an owner’s judgement is genuinely still needed, the flow says so with a task or a decision the terminal gate reads, so completion honestly cannot be reached yet.

A closed case is not necessarily final. A qualifying new signal reopens it under the system actor and re-evaluates it. A case closed as a duplicate is never reopened; the signal follows its primary instead. Nor is a collections case — reopening one would resurrect a campaign whose contact budget was already spent, so a fresh delinquency opens a new case that inherits what the old one used.

How a person intervenes

There are four different things a person can act on, and they are not interchangeable. Confusing the task with the case is the most common reason a request turns out not to mean what it sounded like.

THE TASKComplete it with an outcomeSay you cannot do itHand it to someone elseThe ordinary way a case moves.The outcome is what the nextstep reads, so it is a decision,not a status update.THE STEPRecord a decisionGrant an approvalAttach evidenceSkip the stepA skip may only go where thiscase’s own flow version alreadyroutes from where it stands.THE CASESnooze until a dateEscalateSend to supervisor reviewChange the ownerClose it, or reopen itEach is a status the case statemachine permits, with the datathat transition requires.THE FLOW ITSELFDraft a new versionValidate itActivate itRetire itGoverned, audited, and it nevertouches a case already running.Cases keep the version theystarted on.
Four things a person can act on. A skip may only go where this case's own flow version already routes from where it stands.

A worker who cannot finish the current step’s work has one honest thing to say, and saying it must not strand the case — so the release is treated as a request for recovery rather than as an ending. The task is held, nobody else is offered it, and exactly one recovery review is raised, deliberately sent somewhere other than the team that could not finish the work. Who gets the work covers that lane in full.

A person cannot cancel the work of the step the case is standing in — the engine points them at complete, complete-as-unable, or reassign instead, because cancelling the thing a step is waiting for is how a case gets stranded silently. They cannot skip to an arbitrary step, only to one the flow already routes to. They cannot approve their own prepared request, and they cannot override a compliance hard stop at all.

Two properties matter when designing around this. Case-level moves are separate from flow evaluation: snoozing, escalating, sending to supervisor review and changing the owner all move the case without re-evaluating its workflow, because none of them is a statement about the work the step is waiting for. A snooze resuming does re-evaluate, and so does closing. And a running case keeps its flow version: there is no command that moves one onto a newer definition.

A manual case has no workflow at all: no steps, no routes, no timers, no effects. Its work is the checklist its people write, and it closes through the same guards as any other case once nothing on it is open. It is where work goes when no route fits, so a person can continue by hand rather than a case being held open by a machine that cannot move it.

When a case cannot move

The engine assumes flows will sometimes be wrong. A configuration is written by a person, and a case that cannot move is a real borrower waiting. So there is a layer underneath whose whole job is to notice, and to put a name against work that has stopped.

What goes wrong What catches it
A worker cannot finish the step’s work The recovery lane. The task is held rather than abandoned, and one review goes to somebody who can decide.
Something makes the whole flow moot A reaction, written once for the flow rather than patched into every step it could interrupt.
A step is left behind with work still open Leaving a step withdraws the work it was asking for, with the exit as the audited reason.
A case stops moving for any reason A sweep gives the engine its own chance first, then asks five named questions: has the workflow ended while the case is still open, is the exit satisfied with the case still standing there, is work armed with nothing to show for it, did work end without a continuation, and did an effect fail with no review. Each check is a statement about work the case was owed and does not have — deliberately not a general theory of stuckness, which flags healthy cases and misses real ones.
A case is watching and waiting A standing timer raises its review on a cadence, and new outside evidence pulls that review forward instead of waiting for the next cycle. Between reviews the case reads as scheduled, never as an orphan.
A flow loops on itself at runtime The engine notices it has already been at this step in this pass, records the loop as a defect in the flow rather than spending its budget silently, and gives the owning queue work naming it.
The flow itself is edited Nothing changes for cases already running.

A refund, all the way through

Rosa Iyer paid her Cedar loan off by direct debit on the same day her cheque cleared. The loan ended up holding money that was hers. Nobody reported it — the Lending Core noticed the credit balance and said so, and that opened a payment investigation.

Here is the whole flow she walks. Two people touch it, deliberately two different people. Everything else is the engine.

WhathappenedengineAccountagreesengineWait out thereturn riskwaitsSend themoney backpersonSecondapprovalpersonWait for themoney to landwaitsTell theborrowerengineCloseengineIf the payout fails at the sixth step,the step raises its own exception review and a person is asked to put it right.
Eight steps. Nothing closes until four things are true at once: the loan holds none of her money, the refund was approved and executed, the payout actually settled, and she was told. Having no work left is not one of the four.

Reading one step from the flow

This is the sixth step, as it is written — the whole page in eight lines.

"stageKey": "AWAIT_PAYOUT",
"waitStatus": "WAITING_ON_LMS",
"taskTemplates": [{
  "taskType": "REVIEW_REFUND_EXCEPTION",
  "autoCreateWhen": { "refundPayoutState" is one of
                      FAILED, REJECTED, POSTED_FAILED_REVIEW, CANCELLED }
}],
"exitCriteria":   { "refundPayoutState" is POSTED },
"next":           [ then go to "Tell the borrower" ]

Read it as English. While you sit here, the case is waiting on the Lending Core. If the payout fails, raise a review. You may leave once the payout is posted, and then go and tell the borrower. Every part of that is a rule the engine enforces, and none of it is code.

An interrupt, mid-journey

Maya Chen’s autopay was returned after she switched banks. The Lending Core raised the delinquency, a collections case opened, and the voice agent worked it: a call, a promise to pay negotiated inside the configured limits, and the callback she asked for. Then a bankruptcy notice arrived while the campaign was live.

Nothing about what follows is special-cased. Every move is one of the mechanisms already described.

THE COLLECTIONS CASESignalReviewCallPromiseNot eligibleto contactA bankruptcy notice arrivesThe blocks are applied as the signal isrecorded, before any review is accepted.THE BANKRUPTCY CASE, ITS OWN FLOWProtectVerifyClaimPostureMonitorReleaseThe protection comes off here,as a case effect, only on the routes that reach it.Two tasks wait at Protect:put the hold in place, and review outreach already sent.
Two cases, one borrower. The protection is applied by the signal itself, held by the bankruptcy case, and released by a case effect on a later step of that flow. The collections case is never told to stop; it simply stops being eligible to contact.

Three things in that picture are the pattern rather than the exception.

  • Protection does not wait for a review. A bankruptcy or cease signal blocks the exact affected scopes as the signal is recorded, before any review work is accepted. Verification happens afterwards, and the borrower is protected while it does.
  • The blocks belong to a case, not to a borrower. Releasing them is a case effect on the case that applied them, so another open case protecting the same person keeps its own.
  • Ending a case and unprotecting a borrower are different acts. Only the routes that reach the release step lift the protection. A case that closes some other way leaves it standing, because “this file is finished” and “it is fair to press this person again” are not the same statement.

Changing a flow safely

Flows are business configuration. Editing one does not need a release. It does need a version, and it goes through a governed surface: draft, validate, activate, retire. Drafting and activating are separate permissions on purpose.

Every save runs full schema validation plus catalog-aware checks — every task type, outcome, status and queue named in the definition must resolve against the live catalogs, so the validator never accepts a reference the engine cannot resolve. On top of that, a set of static checks refuses shapes that strand cases.

The check refuses Because
A route to a step that does not exist Everything else reasons over the graph, and a graph with an edge to nowhere cannot be reasoned over at all.
A step with no way out, or a self-loop with no interrupt A case that enters can never leave.
A step that cannot reach an ending The same reason, one level up.
A rule that reads “unable to complete” on a task That release is a recovery request, never a step result, so the branch is one nothing can take.
Work whose condition already satisfies the step’s own exit The step becomes free to leave at the instant the work is called for, so the work is withdrawn as fast as it is raised.

Activating a new version retires the previous one in the same transaction, repoints the case type’s default only where that default followed the same flow, and records a configuration decision. Cases already running stay on the version they started on, and a discarded draft is retired rather than deleted, because configuration history is never hard-deleted.

Every flow described here, and every other flow in the product, runs through one generic evaluator. There are no per-case-type workflow classes — which is why a new case type is a configuration exercise rather than an engineering project.
Unified search across guides, recipes & the API referenceEsc