Workforce & worker registration
Provision human servicing users and register AI workers in one workforce registry, bound by queue, task-type, skill, capacity, fact, tool, and action grants.
Routing, reservation, autonomy, and attribution all resolve against a worker record. A human login and an AI agent are registered through different endpoints because they are provisioned differently, but both become workers subject to the same eligibility rules.
Two registries, one workforce
| Resource | Registers | Returned by |
|---|---|---|
/v1/servicing/users |
A composed human login plus its HUMAN worker, presence, and eligibility profile. |
GET /v1/servicing/users and /users/{userId}. |
/v1/servicing/workers |
An AI worker registration bound to an adapter contract. | GET /v1/servicing/workers and /workers/{workerId}. |
GET /v1/servicing/workers returns AI registrations only. HUMAN and SYSTEM workers are deliberately absent from that collection — a human is administered as a user, not edited as an agent registration.
Provision a human user
Creating a user is one atomic operation. It creates the login, the HUMAN worker, initial presence, and the queue and task-type eligibility the router reads:
curl -X POST "$BASE/v1/servicing/users" \
-H "Authorization: Bearer $TOKEN" \
-H "LendEasy-Tenant: demo-lender" \
-H "Content-Type: application/json" \
-d '{
"username": "maria.agent",
"temporaryPassword": "<write-only>",
"firstName": "Maria",
"lastName": "Agent",
"email": "maria@example.invalid",
"officeId": 1,
"roleIds": [7],
"workerProfile": {
"queueCodes": ["COLLECTIONS"],
"taskTypeCodes": ["COLLECTIONS_OUTREACH"],
"skills": ["COLLECTIONS"],
"languages": ["en"],
"capacity": 8
}
}'
{
"userId": 4101,
"username": "maria.agent",
"displayName": "Maria Agent",
"active": true,
"resourceId": 4101
}
temporaryPassword is write-only and never appears in a read. Reads return a safe projection — identity, office, roles, active state, credentialResetRequired, and the worker profile — without internal login keys or credential material.
PUT /v1/servicing/users/{userId} replaces the mutable administration and worker-profile fields. username and the public user ID are immutable; deactivation is active: false, not deletion, so historical attribution survives.
Register an AI worker
An AI registration declares the adapter contract it speaks and the exact authority ceiling it may exercise:
curl -X POST "$BASE/v1/servicing/workers" \
-H "Authorization: Bearer $TOKEN" \
-H "LendEasy-Tenant: demo-lender" \
-H "Content-Type: application/json" \
-d '{
"workerCode": "COLLECTIONS_VOICE_PRIMARY",
"displayName": "Collections voice agent",
"adapterCode": "OUTBOUND_VOICE_V1",
"contractVersion": "1.0",
"credentialRef": "voice-agent-client",
"readinessEvidenceRef": "eval-2027-05-04",
"queueCodes": ["COLLECTIONS"],
"taskTypeCodes": ["COLLECTIONS_OUTREACH"],
"skills": ["COLLECTIONS", "VOICE"],
"languages": ["en"],
"capacity": 20,
"factGroupGrants": ["LOAN", "OVERDUE", "PAYMENT", "PAYMENT_INSTRUMENT", "CONTACT", "RESTRICTION"],
"toolGrants": ["CHECK_CONTACT_ELIGIBILITY", "SUBMIT_VOICE_DISPOSITION"],
"eligibleActionCodes": ["AR_002_CREATE_PROMISE_TO_PAY", "AR_007_PREPARE_BORROWER_AUTHORIZED_PAYMENT"],
"active": true
}'
Three grant lists define the ceiling, and each is enforced separately at runtime:
| Grant | Bounds | Example |
|---|---|---|
factGroupGrants |
Which fact groups may enter the agent’s work packet. | RESTRICTION without PAYMENT_INSTRUMENT. |
toolGrants |
Which declared tools the session may call. | CHECK_CONTACT_ELIGIBILITY. |
eligibleActionCodes |
Which typed actions the agent may prepare at all. | AR_002_CREATE_PROMISE_TO_PAY. |
credentialRef and readinessEvidenceRef are references, not secrets: reads return the registration without resolving credential material or adapter deployment configuration. PUT /v1/servicing/workers/{workerId} replaces every mutable field and revalidates the whole contract before the registration is activated again — a partial edit cannot leave a worker active against a contract that no longer validates.
Self-scoped reads and presence
Two routes are scoped to the authenticated worker rather than to administration.
GET /v1/servicing/me returns identity, queue and task-type eligibility, presence, the live reservation if one exists, active assignments, and capacity state in one call — the read a workspace should ground itself on instead of caching a client-side profile. capacity, activeReservation, and per-assignment summary and dueAt keys are omitted when absent rather than returned as null.
POST /v1/servicing/workers/me/presence sets the caller’s own availability and channel set:
{
"state": "AVAILABLE",
"channels": ["PHONE", "EMAIL"]
}
Presence is an eligibility input, not a schedule. Setting AVAILABLE does not create capacity beyond the profile’s capacity, and it cannot make a worker eligible for a queue or task type the profile does not carry.
GET /v1/servicing/me/events is the worker’s doorbell: a server-sent event stream that tells the workspace when something changed for this worker — an offered reservation, an assignment, a ringing callback — so the client reacts to a push instead of polling every few seconds. The stream carries change notifications, not work payloads; on a ring, the workspace re-reads GET /v1/servicing/me.
Permissions
| Operation | Permission |
|---|---|
| Read users | READ_SERVICING_USER |
| Create a user | CREATE_SERVICING_USER |
| Replace a user profile | UPDATE_SERVICING_USER |
| Read AI workers | READ_SERVICING_WORKER |
| Register or replace an AI worker | CREATE_SERVICING_WORKER / UPDATE_SERVICING_WORKER |
| Read own worker context | READ_SERVICING_TASK |
| Set own presence | UPDATE_SERVICING_WORKER_PRESENCE |
Administering the workforce and doing the work are separate authorities. Holding UPDATE_SERVICING_WORKER does not let an operator raise their own eligibility to reach restricted work, and no route lets a worker widen their own profile.
Continue with AI agent workforce for the session model, and Smart queues & routing for how these records rank and reserve work.