Documents & secure upload
A document is inert until it has been scanned, classified, and reviewed — and what a reviewer accepts is provably the same file that came through the gate.
Every document that reaches servicing — hardship proof, paystubs, identity documents, court filings, certificates — is scanned, classified and routed by the document agent, and lands on the case as evidence the workflow can act on. Where a document type has an extraction profile, the agent reads it field by field and the platform re-derives every value before accepting it. So it stays inert until it has been checked: bytes and metadata are declared in the same controlled intake, the file is scanned and classified before anyone reads it, and it is unavailable to workers and AI until security processing completes. Later corrections touch links and titles rather than content, so the file a reviewer accepted is provably the file that came through the gate.
Metadata and bytes arrive together
The API accepts metadata and bytes as one controlled multipart intake at POST /v1/servicing/cases/{caseId}/documents. It returns the numeric documentId, scan/review state, and any review task; content is unavailable until the security and review gates permit it. Declaring the type and the case in the same call as the file is what stops an upload from existing as an orphan blob that nobody claimed and no workflow is waiting on.
curl -X POST "$BASE/v1/servicing/cases/1001/documents" \
-H "Authorization: Bearer $TOKEN" \
-H "LendEasy-Tenant: demo-lender" \
-H "Idempotency-Key: bankruptcy-notice-7781" \
-F 'metadata={
"title": "Bankruptcy notice filed June 14",
"documentType": "BANKRUPTCY_NOTICE",
"origin": "BORROWER_UPLOAD",
"sensitive": true,
"loanRefs": ["7204"]
};type=application/json' \
-F "file=@bankruptcy-notice.pdf;type=application/pdf"
A clean scan is not a true document
Controls validate declared size, actual bytes, digest, media type, file structure, malware, encryption policy, and archive contents. A clean scan means the file passed those controls. It says nothing about whether the employer named on the paystub exists or the figures on it are real, which is why acceptance is a separate decision: the document agent extracts and checks the figures the case policy needs, and a person accepts the evidence only where that policy reserves the decision.
Rejected evidence stays, under restricted access
POST /v1/servicing/documents/{documentId}/review records accepted, rejected, or approved reclassification plus reviewer and note. Accepted evidence can support a case decision; rejected evidence remains in history with restricted access and retention policy. Deleting what was turned down would erase the reason a borrower was asked for the document twice.
Three reads, and two show accepted only
A document is registered on a case, but evidence is usually looked for by the customer or loan it concerns. Three reads cover that, and their scopes differ deliberately:
| Route | Returns |
|---|---|
GET /v1/servicing/cases/{caseId}/documents |
Everything registered on one matter, at any lifecycle state. |
GET /v1/servicing/customers/{customerRef}/documents |
Accepted servicing documents across the customer’s visible cases. |
GET /v1/servicing/loans/{loanRef}/documents |
Accepted documents explicitly linked to that loan. |
The two cross-case reads return accepted documents only. A quarantined, rejected, or still-scanning file does not leak into a customer- or loan-level view — it stays visible on its own case, where the reviewer who owns it can see why. All three accept the same filters: documentType, origin, sensitive, and a creation window, with limit/offset paging.
A document reaches a loan only through an explicit link, so a customer-level filing does not silently attach itself to every loan on the account. Reviewers correct those links in one step:
PUT /v1/servicing/documents/7001/loan-links
{
"loanRefs": ["7204", "7205"],
"reason": "Reviewer confirmed both loans are covered by the filing."
}
The supplied list becomes the complete set of loan links; the required case and customer links and the historical retention scope are untouched. PUT /v1/servicing/documents/{documentId} separately corrects approved metadata such as an intake title, and explicitly cannot change content, origin, checksum, review evidence, or links. Both commands require a reason, and neither replaces the bytes or re-runs the security gate — the file that was scanned is the file that stays.
POST /v1/servicing/documents/{documentId}/archive retires a document under retention and hold rules while preserving its audit identity, links, and scan history.
Four ways a document got here
Where a file came from decides how much weight a reviewer can put on it, so the origin is recorded as evidence rather than inferred from the filename:
| Source | Key evidence |
|---|---|
| Customer upload | Authenticated/verified upload session, request, time, and hash. |
| Worker upload | Actor, case, reason, source, and hash. |
| Provider document | Provider reference, signature/transport verification, and normalized type. |
| System-generated | Template version, fact snapshot, render hash, and generator time. |
A download link is issued once, to one reader
The implemented GET /v1/servicing/documents/{documentId}/content evaluates current permission and returns a no-store, short-lived download plus checksum; every sensitive access is audited:
{
"downloadUrl": "https://content.example/opaque-signed-token",
"expiresAt": "2027-05-05T18:35:00Z",
"fileName": "bankruptcy-notice.pdf",
"contentType": "application/pdf",
"checksumSha256": "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
}
The download is issued only for scan-clean, authorized content. downloadUrl is opaque and short-lived: a URL stored as a document reference outlives its permission, and one passed to a colleague makes the access record name the wrong reader. Re-request it and take the audit entry — the request is cheap and the record stays honest. Verify checksumSha256 against the bytes you receive. AI packets receive only explicitly permitted document content or extraction, never an ambient case-folder mount.
Retention, holds, and disposal
Retention policy considers document class, legal entity, case state, disputes, investigations, and legal hold. Disposal is an auditable lifecycle event, and a hold blocks scheduled disposal. Tying deletion to case closure would destroy the evidence for a matter on exactly the day it stops being worked and starts being examined.