Quick Summary: A person certification is one person’s holding of a certification: its workflow state, issued and expiry dates, identifier, evidence and history. Record it, move it through approval, renew it, and report coverage across a population. Expiry is derived from the date, never from the status — an approved certification stays
approved in storage after its date passes, so every read carries a separate expiry block.Attributes
The detail read adds
attachments: { id, type, label, url, createdAt } where type is file or link and url is set for links only.
Operations
Person path slots ({personId}) accept the person’s email address or the opaque id from any response.
A person’s certifications
The list is filterable by
status (eq / in) and expiry (eq / in over none, valid, expiring, expired); the page total is the count for the same filters. A certification held directly and through a career appears once, with heldDirectly: true and its careers listed.
The history is every superseded attempt — archived by renewal (endedAs: "expired") or by resubmission after a denial (endedAs: "denied") — each with its dates, identifier, who assigned, submitted, approved or denied it and when, and the evidence that stayed with that attempt. Read it with the live record for the full story.
Recording and maintaining
Recording.
certificationId is required. With nothing else the record is the light assign — it lands in assigned with no dates, identifier or evidence. Add issuedAt, expiresAt, identifier, scoreDescription, certified, issuingAuthority, one link and up to ten files to record it in full. Each file returns a pre-signed upload URL in uploads; upload the bytes to it. A person who already holds the certification is a 409 — update or renew instead.
Two fields can be set here that no app screen can set: scoreDescription, and certified: false. The app reads both, so API-recorded certifications may carry values app-recorded ones never do.
The state machine. assigned → awaiting_approval (submit) → approved or denied; denied → awaiting_approval (submit again); approved is terminal except through renewal. Approve also accepts assigned directly, matching the app’s manager path. A transition the current state does not allow is a 409.
Updating is allowed while assigned, awaiting_approval or denied; an approved certification is closed to edits (409) and must be renewed. Omitted fields are untouched; an explicit null clears issuedAt, expiresAt, identifier, scoreDescription or issuingAuthority. expiresAt may not precede issuedAt.
Renewing archives the current attempt and its evidence to history as expired, resets the record to assigned, and records the new issue. The identifier and issuing authority carry forward unless replaced; the previous result does not.
Unassigning removes a direct assignment and the person’s record of it. A certification held through a career cannot be unassigned per person (409, with the careers named) — change the career instead.
Evidence
Attachments are supplied on the record, update and renew writes. The download URL is short-lived, and is issued only for a file attachment that belongs to this person and this certification; anything else is a 404.
Coverage and reporting
All four return the object above, filter by
status and expiry, and accept expiringWithinDays (default 30) to set what counts as expiring. Coverage is relative to the acting person: team is their direct reports, org their whole span of control, company the company (admin and full-access identities only). Search adds certificationId, email (eq, like) and career (like).
The status breakdown answers the compliance question directly:
expiry into status.
Batch and composite
Decisions take
{ decision: "approve" | "deny", items: [{ email, certificationId }] } (up to 200). The call is not atomic: each item is decided on its own and the response lists every item’s outcome (applied or failed, with a reason) in request order.
Composite is the integration-shaped write: { email, certificationTitle | certificationId, issuedAt, expiresAt?, scoreDescription?, renew? }. It resolves the person by email, finds the certification by exact title (creating it in the library, name only, if absent) or by id, records it and approves it directly; renew: true routes through renewal of the current approved record. Restricted to global-administrator acting identities.
Permissions
Reads resolve against the acting person’s visibility of people: admin identities see the company, a manager their span of control, and any person their own record. A person outside that span is a 404, indistinguishable from one that does not exist. Writes resolve against the acting person’s permission to update certification records — span of control for a manager, company-wide for admin identities. Two app roles hold no certification-record permissions at all, and a key acting as one receives insufficient-permissions on every write here.Related errors
- not-found — the person is not in your company or span, or does not hold this certification
- conflict — already held on record; a transition the state does not allow; an approved record on update; unassigning a career-held certification
- validation-error — an unknown
certificationId, an expiry before the issued date, a badscopeorexpiringWithinDays - unprocessable — the composite write could not be applied in the record’s current state