Customer identifiers
Attach government identifiers to a stable customer by supplying the complete value once, expose only normalized masks on ordinary reads, and reveal complete values through audited sensitive routes.
A customer is the party; an identifier is one claim about that party. Names, government numbers, and your own correlation key have different security and verification semantics, so LendEasy does not collapse them into one “verified customer” flag.
Identifier classes
| 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. Do not use a name, email address, or phone number as a relationship key. They change, may be shared, and are sensitive search inputs.
The masking boundary
Supply the complete value exactly once, at write time. LendEasy normalizes it—an SSN or EIN is reduced to nine digits regardless of punctuation—and every ordinary read returns only the type, a masked display, and lifecycle state. Complete values never appear in command audit records, history events, or logs.
collection UI ── complete value ──► POST /v1/customers/{customerId}/identifiers
aggregate read ─► identifierId + identifierType + maskedDisplay + verificationState
sensitive read ─► complete value (both read permissions; audited; never cached)
This boundary prevents a broad customer read, report, log, or support tool from becoming a secret-extraction path, while still letting authorized staff retrieve the value when a regulator or provider requires it.
Add an identifier
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. The value is stored as 601114821 and shows up on the aggregate read as:
{
"identifierId": 66,
"identifierType": "SSN",
"maskedDisplay": "•••-••-4821",
"verificationState": "UNVERIFIED",
"status": "ACTIVE"
}
Notice that the complete value is not echoed back—not in the command envelope, not in the aggregate, not in history.
Verification is set by trusted integrations
Verification belongs to one identifier. Maya may have a verified SSN, a verified email contact point, 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.
Reveal and exact lookup
Two audited routes work with complete values, and both require READ_LMS_CUSTOMER and READ_LMS_CUSTOMER_SENSITIVE together:
GET /v1/customers/{customerId}/sensitivereturns complete identifier values alongside raw contact values and consent evidence. Every call is audited and the response is never cached.POST /v1/customers/lookupfinds customers whose current identifier exactly equals the supplied value. A POST body keeps the value out of URLs and logs; the route accepts noIdempotency-Keyand 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.
Corrections and removal
identifierType is immutable. PUT /v1/customers/{customerId}/identifiers/{identifierId} merge-updates the record—for example attaching an evidenceRef—and a wrong value is corrected by removing the identifier and adding the right one, so historical decisions remain explainable. DELETE is a bodyless soft removal: the identifier leaves the aggregate read, and its masked history is retained in the customer ledger for authorized audit views.
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.