What a key needs
- A Write switch on the field group the write changes. Write implies Read. Write exists for
Personal contact, Personal details, Job history, Pay and Leave, and for Hires, which has a Write switch
only. A key with the Read switch alone answers
write-not-allowed. See Field groups. - A named requester. Every write key names a console user who holds the permission for each write the key may make, for example requesting a pay change. Approval chains that route to “the requester’s manager” start from this person. Nobody approves their own request, so the requester can never approve what the key files.
requestedBy, optionally. A request body may name another requester: an andteam user id or work email, such as the hiring manager in your ATS. That person is checked for the same permission over that employee. If the check fails the request is refused withrequester-not-allowed. It is never put back on the key’s requester.
requester-not-allowed and its
reads keep working.
202 or 201
A write never approves itself.
With a chain, a
*.decided webhook is sent when the chain ends or the request is withdrawn:
job_change.decided, pay_change.decided or leave_request.decided. Its data.requestedVia.keyId
is the key that filed the request, so you can match a decision to your own call. See
Webhooks and Employee events.
Everything the console does
Because a write is the console’s own request:- HRIS locks. A field the tenant’s HRIS owns for this person is refused with
field-locked, which names every locked field. Nothing is written. Correct the field in the HRIS, which owns the record. - Approval chains. The tenant’s chain for the action decides. A step that resolves to nobody is
approval-chain-unresolvable. - Audit. The employee’s history and the request’s approval history record the key as the actor, with the requester it acted for. Every request also has its own row in the tenant’s API audit.
- Notifications. The employee is told what changed or what was requested for them, and by which system.
- Letters. The API never sends a letter. After an approved job or pay change, HR sees a letter to send in andteam.
Check before you send
Every POST that creates something takes?validate_only=true. It answers 200 with what would
happen and writes nothing: the approval steps and who they resolve to, the knock-ons (“3 reports also
move”) and, for leave, the balance after the request.
Every write needs two headers
A POST needs anIdempotency-Key. A PATCH needs If-Match. See Retries and versions.
A body that is wrong is one 422 validation-failed. Its errors
lists every field at once, each with a pointer, a code and a detail, so you fix them in one
round.
Change personal details
PATCH /v1/employees/{id} is a JSON Merge Patch (RFC 7396) of preferredName, personalEmail,
mobile, homeAddress, dateOfBirth and nationality. Each field needs the Write switch of its
group: Personal contact for personalEmail, mobile and homeAddress, Personal details for the
rest. Read the employee first and send its ETag as If-Match.
ETag. A part of homeAddress sent as null is removed;
a part left out is kept. Legal first and last names, work email, Directory and Positions fields and
Special categories are not writable.
Request leave
POST /v1/employees/{id}/leave-requests submits leave in the employee’s name through the leave approval
chain. The balance and per-request caps are checked as in the portal.
awaiting_approval, or 201 with approved when the chain has no steps. A
refusal is 422 validation-failed with a code in errors, such as insufficient-balance or
over-cap. Sick leave is refused (sick-leave-in-portal): the sick note belongs in the portal. The
employee is told which system made the request and can withdraw it in the portal. leave_request.submitted
is sent.
File a job change with pay
POST /v1/employees/{id}/job-changes files a promotion, transfer or regrade with an
effectiveDate. Send the facts that change, and a pay block when pay rides along. The pay change is
filed with the job change in one transaction, so there is never a promotion without its pay change. The
pay block needs the Write switch on Pay as well.
not-supported. A change already waiting for the person is
pending-change-exists, with the existingId.
File a pay change
POST /v1/employees/{id}/pay-changes takes a kind (increase, promotion or adjustment), an
effectiveDate, the whole package as components[], and a reason kept as the change’s note. The
package is checked against the tenant’s compensation rules.
Withdraw a request
POST /v1/job-changes/{id}/withdraw, POST /v1/pay-changes/{id}/withdraw and
POST /v1/leave-requests/{id}/withdraw take back a request while it waits. Only the key that filed it
may withdraw it. Anything else is not-withdrawable. Each withdraw
needs an Idempotency-Key. The matching *.decided event is sent with status: "withdrawn".
Read a request back at any time with GET /v1/job-changes/{id}, GET /v1/pay-changes/{id} or
GET /v1/leave-requests/{id}.
New hires
An ATS starts a hire withPOST /v1/hires. That is a draft offer for HR, not a starter. See
New hires from an ATS.