LendEasy/DocsLMS + Servicing·v1
Start integrating
Guides›Borrowers & Customers›Customer identities

Customer identifiers

A government ID is supplied in full exactly once and never echoed back — ordinary reads see a mask, and the full value comes only from an audited route that records who looked.

A single “verified customer” flag is the tidiest thing you can build and the least defensible thing you can rely on: it makes every later decision trust whichever check behind it was weakest. So LendEasy does not collapse identity into one boolean. A customer is the party; an identifier is one claim about that party, and names, government numbers, and your own correlation key carry different security and verification semantics.

Three identifier classes, three exposure rules

Class Example Stored on customer record Ordinary response
Numeric LendEasy ID customerId 412 Generated relationship key Full value (serialized as "412")
Your external ID crm-person-81042 Caller-controlled correlation key on the profile Full caller-controlled value
Government identifier U.S. SSN, EIN, DRIVER_LICENSE, PASSPORT Complete value, normalized at write time Type, mask, lifecycle, verification state

INDIVIDUAL customers use SSN, DRIVER_LICENSE, or PASSPORT; CORPORATE customers use EIN. A name, email address, or phone number makes a poor relationship key: two people in one household share an email, a married borrower changes a surname, and a phone number is reassigned to a stranger within months. Keyed that way, a portfolio quietly merges two customers into one and splits a third in half.

Supplied once, and never echoed back

Supply the complete value exactly once, at write time. LendEasy normalizes it: an SSN or EIN is reduced to nine digits regardless of punctuation.

INSIDE THE BOUNDARYCollection UI601-11-4821complete valueexactly oncePOST /v1/customers/{customerId}/identifiersnormalized at write time601114821stored complete, never echoedCommand receipts, history events,reports and logs: mask onlycustomer read•••-••-4821type, mask, status,verification statesensitive readComplete valueboth permissions,audited, never cached
One arrow in, one audited arrow out, a mask everywhere between — which is why a report or a support tool cannot extract a secret.

Punctuation goes in; nine digits are stored

curl -X POST "$BASE/v1/customers/412/identifiers" \
  -H "Authorization: Bearer $TOKEN" \
  -H "LendEasy-Tenant: demo-lender" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: maya-ssn-v1" \
  -d '{
    "identifierType": "SSN",
    "value": "601-11-4821"
  }'
{ "resourceId": 66 }

Requires UPDATE_LMS_CUSTOMER_IDENTIFIER. Normalizing at write time is what makes an exact lookup work however the caller typed it, so the same person is not filed twice under two punctuations. The customer read shows:

{
  "identifierId": 66,
  "identifierType": "SSN",
  "maskedDisplay": "•••-••-4821",
  "verificationState": "UNVERIFIED",
  "status": "ACTIVE"
}

Verification is set by trusted integrations

There is no public verify route. Identity-verification providers write verification state through trusted integrations; API callers can attach an evidence reference but cannot assert verification.

Verification belongs to one identifier. Maya may have a verified SSN, a verified email, and an unverified residence at the same time. A decision names which facts and assurance level it requires instead of trusting a customer-wide boolean.

Two audited routes touch complete values

Both require READ_LMS_CUSTOMER and READ_LMS_CUSTOMER_SENSITIVE together:

  • GET /v1/customers/{customerId}/sensitive returns complete identifier values alongside raw contact values and consent evidence.
  • POST /v1/customers/lookup finds customers whose current identifier exactly equals the supplied value. A POST body keeps the value out of URLs and logs; the route accepts no Idempotency-Key and answers with masked summaries only.
curl -X POST "$BASE/v1/customers/lookup" \
  -H "Authorization: Bearer $TOKEN" \
  -H "LendEasy-Tenant: demo-lender" \
  -H "Content-Type: application/json" \
  -d '{
    "identifierType": "SSN",
    "value": "601114821"
  }'
{
  "items": [{
    "customerId": "412",
    "status": "ACTIVE",
    "customerType": "INDIVIDUAL",
    "displayName": "Maya Chen",
    "lastModifiedAt": "2026-09-15T14:22:08Z"
  }]
}

An empty result never reveals whether a hidden match exists. A lookup that distinguished “no such customer” from “a customer you may not see” would be an oracle: a caller could confirm somebody’s identifier one probe at a time without ever being allowed to read it.

A wrong value is replaced, not edited

identifierType is immutable. PUT /v1/customers/{customerId}/identifiers/{identifierId} merge-updates the record—attaching an evidenceRef, for example—but a wrong number is corrected by removing the identifier and adding the right one. Editing it in place would leave every decision made on the old value looking as though it had been made on the new one. DELETE is a bodyless soft removal: the identifier leaves the customer record read, and its masked history is retained in the customer ledger for authorized audit views.

Never put a government identifier in externalId, a note, a form field, an idempotency key, a URL, or an error message. Those values are designed to be operationally visible; the identifier routes are the only place complete values belong.
Unified search across guides, recipes & the API referenceEsc