Quick Summary: People are the company’s workforce records. What a caller can see and change is always bounded by the acting identity behind the credential — a manager’s credential sees their span of control, an admin’s their departments.
Attributes
Email uniqueness is global: creating a person — or changing an email — with an address already registered to another SkillsDB company returns a 409 conflict whose message never names the holder.
Addressing a person
Every/people/{id}-style path slot on this API takes the person’s email address — the primary, documented form — or the opaque id from any response (ids are all digits, so the two forms never collide). Request fields that reference a person are email fields: managerEmail on create, { "email": ... } on the manager operation, delegatedApproverEmail on trainings. Resolution is bounded by your company — an email held by another company is indistinguishable from one that does not exist. Where the identifier is a path segment (/people/{id}), that is a 404; where it is a request field (managerEmail, people[].email, emails, delegatedApproverEmail), it is a 400 naming the field with code unknown_person — a 404 on an existing route would read to generated clients as the route being missing. If your data holds case- or whitespace-variant duplicates of one address, the API answers 409 rather than pick a record: resolve the duplicates, or use the opaque id.
Access levels
Four levels are assignable —basic, manager, admin, fullaccess — and three are legacy (executive, globaladmin, director): readable on people who hold them, never writable. GET /api/v1/access-levels publishes the full set.
Updates are truly partial
PATCH /api/v1/people/{id} is field-presence based: absent means unchanged, explicit null means clear. Email, access level, manager, job title, people type and active status each move through their own operation (/email, /access-level, /manager, /job-title, /people-type, /activate, /deactivate) — updating a phone number never requires knowing or restating anything else. The manager operation takes { "email": "lead@example.com" } — the manager’s email address, or null to clear the manager.
Reporting structure
GET /people/{id}/direct-reports— one level only.GET /people/{id}/span-of-control— the whole subtree, paginated, each entry carryinglevel. The subject is included at level 0, so a headcount over the rows is off by one if that is forgotten.PATCH /people/{id}/managerrefuses a reporting loop with a 409 carryingmanagerPersonId,managerNameandreportDepth;GET /people/managers?excludeSubtreeOf=ada@example.com(an email, or an opaque id) prevents selecting one in the first place.
Org schema
Eleven independent many-to-many dimensions: businessUnits, divisions, businessRegions, countries, states, cities, sites, buildings, departments, teams, shifts.POST /people/{id}/org-schemais additive only — it never removes a membership, and ids already held are ignored.DELETE /people/{id}/org-schemaremoves named memberships.PUT /people/{id}/org-schema/{dimension}replaces one dimension with exactly the supplied list — the shape an HRIS sync needs.
Additional skills
A person’s directly-assigned skills — distinct from the ones a career confers, and the relationship recording a training requires:GET /api/v1/people/{personId}/skills— the direct assignments, each withid,name,descriptionandisCertification.POST /api/v1/people/{personId}/skillswith{ "skillIds": ["512"] }— assigns existing catalogue skills. Already-assigned skills are skipped, never re-graded; the response reportsaffectedandskipped.DELETE /api/v1/people/{personId}/skillswith the same body — removes direct assignments (with their grades and flags), writing history first. Career-conferred skills are managed through the career, not here.
skills:read / skills:write scopes — the assigned object’s scope governs, the same convention career assignment uses. Who may assign follows the app’s own rule: a key acting as the person assigns to themselves, a manager within their span, an admin anywhere in the company.
No delete
Deleting a person does not exist on the API, deliberately: the product’s delete is an irreversible hard delete across roughly fifty tables.POST /people/{id}/deactivate is the lifecycle operation — it retains the person and their history.
SCIM and this API are alternatives, not layers. If your identity provider provisions people through SkillsDB’s SCIM integration, do not also sync people through this API against the same tenant.