Skip to main content
Quick Summary: An assignment links a person to a career at a level. Assigning grants that level’s learnings and attaches the career’s skills; unassigning records career history first, so a person’s past careers remain readable.

The assigned person

Returned by GET /api/v1/careers/{id}/people.
The two dates have different owners. assignedAt is stamped by SkillsDB at the moment of the write and cannot be supplied; initiationDate is yours, and records when the person actually begins in the career.

The person’s career

Returned by GET /api/v1/people/{personId}/careers. Previous (ended) assignments are included by default — pass ?includePrevious=false to exclude them.

The distribution

Returned by GET /api/v1/careers/{id}/distribution. Counted server-side — do not page through the assigned-people list to derive these numbers. Each bucket is { id, label, count }, where id is null for the catch-all bucket (for example No Flag).
A credential bound to a manager sees only their span of control, so these counts reflect what the acting person is permitted to see — not necessarily the whole company.

Two rules that surprise integrations

Assigning is never an update. POST /api/v1/careers/{id}/people takes a list of people, each at their own level. Anyone already holding the career is skipped silently — no error, and no level change. If you need to move someone, use PATCH /api/v1/careers/{id}/people/{personId}; an integration that re-assigns expecting a correction will do nothing at all. Assigning fans out. Beyond the assignment row, it grants every learning attached to the target level and attaches the career’s skills to the person. One call against a hundred people is a large write, not a small one — budget it against your rate limit. Both assign and unassign accept at most 500 people per call — a larger array is rejected with a 400 validation-error before anything is written, so split bigger batches into chunks.