Authentication
A token says which machine is calling; it never says what that machine may do — the endpoint's named permission decides that, on every call.
A token says which machine is calling. It never says what that machine may do — that is a separate check on every endpoint, and treating a broad scope as permission is how a reporting client ends up able to approve a funding.
A scope opens a surface
- Origination, money movement, governed commands
- The same breadth for every client holding it
A permission decides an operation
- One named operation, shown on its API page
- Checked again on every call
Client-credentials flow
LendEasy uses OAuth 2.0 client credentials, each bound to one tenant and one environment. Exchange them only from a trusted backend:
curl https://auth.lendeasy.ai/oauth2/token \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=client_credentials" \
--data-urlencode "scope=lending:read lending:write"
{
"access_token": "eyJ...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "lending:read lending:write"
}
Send the token on every ordinary client request:
curl https://sandbox.api.lendeasy.ai/v1/loans/7204/summary \
-H "Authorization: Bearer $TOKEN" \
-H "LendEasy-Tenant: demo-lender"
Every ordinary client call also carries the tenant selector header LendEasy-Tenant — lowercase letters, digits, and hyphens, for example demo-lender. See Environments for how tenants are provisioned. Closed connector callbacks use X-LendEasy-Gateway-Signature or the dispatch-bound X-LendEasy-Voice-Task-Token instead.
Cache the token; never decode it for logic
Cache the token until shortly before expiry, refresh when less than 10% of expires_in remains, and serialize refreshes — a service that lets every thread refresh at once stampedes the token endpoint at exactly the moment it is least able to tolerate a failure.
| Rule | Why it matters |
|---|---|
| Keep secrets in a secrets manager. | Source control, logs, and client applications are not secret stores. |
| Use a different client per workload. | Revocation and least privilege stay narrow. |
| Never log tokens or client secrets. | They authorize API calls until revoked or expired. |
| Do not decode a token to drive business logic. | Claims may evolve; API responses are the contract. |
| Rotate credentials without changing client IDs. | Overlapping secrets allow zero-downtime rotation. |
A scope is a surface, not a permission
Five scopes, each a doorway rather than an authority:
| Scope | Typical use |
|---|---|
servicing:read |
Cases, tasks, decisions, interactions, and evidence metadata. |
servicing:write |
Create cases, interactions, promises, and operate opened action requests. |
lending:read |
Customers, products, loans, schedules, payments, and statements. |
lending:write |
Origination, customer changes, money movement, and governed commands. |
actions:approve |
Second-person approval for configured high-impact actions. |
The header selects; the token authorizes
The token must be entitled to the tenant its call names. A resource from another tenant behaves as unavailable, and Sandbox tokens are rejected by Production exactly as Production tokens are rejected by Sandbox.
| Response | Meaning | Recovery |
|---|---|---|
401 Unauthorized |
Token is absent, expired, malformed, or issued for another environment. | Refresh once, then stop and surface the authentication failure. |
403 Forbidden |
The client is authenticated but lacks the required scope or permission. | Change the client’s assigned role; retries do not help. |
404 Not Found |
The resource does not exist or is outside the caller’s tenant visibility. | Check the opaque ID and environment without probing other tenants. |
Authentication never bypasses maker-checker
When an operation requires approval, the creator and approver must be distinct eligible actors. Approval records preserve the actor, decision, policy version, request hash, and time. See Access control & maker-checker.