The API
One key per integration, with the scopes it needs and nothing else. Keys are made in Settings → API, shown once, and every call is logged with what it did.
Calling it
Send your key as Authorization: Bearer staffena_…. Everything is scoped to the company the key belongs to — there is no parameter for choosing a company, because a key that could would be a key worth stealing.
Writes take an Idempotency-Key header. Send the same key twice and the second call replays the first answer rather than doing it again, which is what makes a retry safe after a timeout.
People and their records
GET/api/v1/peoplescope people:read
Everybody on the books.
The directory another system matches its own records against. Deliberately not the whole row: pay, documents and anything about somebody’s private life are not a directory read.
Parameters
limit- How many rows, up to 200. e.g. 100
offset- Where to start, for the next page. e.g. 100
department- One department, by its id.
office- One office, by its id.
active- true for people still employed, false for leavers.
since- Only what changed after this moment — how a nightly sync avoids the whole company. e.g. 2026-09-01T00:00:00Z
What comes back
id- Ours. Stable for ever.
employeeId- The company’s own number for them.
fullName- As the company writes it.
email- Their work address, where there is one.
jobTitle- What they are called.
departmentId- Their department, by id.
officeId- Their office, by id.
employmentType- Full time, part time, and whatever else the company uses.
startedOn- Their first day.
endedOn- Their last, once there is one.
active- Whether they are still employed.
updatedAt- What `since` compares against.
Leave
GET/api/v1/leavescope leave:read
Leave that has been asked for and decided.
For a rota system that needs to know who is away, and for a data warehouse counting absence. Balances are not here: a balance is computed from a policy, and a number copied out of it goes stale the moment somebody books a day.
Parameters
limit- How many rows, up to 200. e.g. 100
offset- Where to start, for the next page. e.g. 100
person- One person, by their id.
status- pending, approved, rejected or cancelled.
from- Leave ending on or after this date. e.g. 2026-01-01
to- Leave starting on or before it. e.g. 2026-12-31
What comes back
id- The request.
staffId- Whose it is.
type- The leave type’s key, as the company named it.
startsOn- First day away.
endsOn- Last day away.
days- Working days, as this company counts them.
status- Where the request got to.
Attendance
GET/api/v1/attendancescope attendance:read
When people started and stopped.
Sessions rather than punches, because a session is the thing with a length. A door system writing punches in uses the device endpoint instead, which authenticates a machine rather than an integration.
Parameters
limit- How many rows, up to 200. e.g. 100
offset- Where to start, for the next page. e.g. 100
person- One person, by their id.
from- Sessions starting on or after this moment.
to- Sessions starting on or before it.
What comes back
id- The session.
staffId- Whose it is.
startedAt- When they started.
endedAt- When they stopped, or null while they are still in.
source- Where it came from: the browser, a terminal, a correction.
POST/api/v1/attendancescope attendance:write
Punches from a machine.
A batch of up to a thousand, which is how a terminal uploads a day at a time rather than making a request per person. Send the same batch twice with the same idempotency key and the second one changes nothing.
Parameters
punches[].cardRef- The card or employee number the machine read.
punches[].at- When it happened, as an instant. e.g. 2026-09-20T07:58:00Z
punches[].direction- in or out. Anything else is read as in.
Idempotency-Key- A header. The same key replays the first answer rather than writing twice.
What comes back
sessions- How many sessions the batch made.
duplicates- Punches already known, ignored rather than doubled.
held- Punches naming nobody, kept for somebody to resolve.
Pay
GET/api/v1/payscope pay:read
Published payslips.
Totals rather than lines, and published ones only: a draft is a figure somebody is still editing, and an accounting system that imported one would be reconciling against a number that changed.
Parameters
limit- How many rows, up to 200. e.g. 100
offset- Where to start, for the next page. e.g. 100
person- One person, by their id.
month- One month, as the payslip period. e.g. 2026-09
What comes back
id- The payslip.
staffId- Whose it is.
periodStart- The month it is for.
gross- Before deductions.
deductions- What came off.
net- What was paid.
currency- What it was paid in.
POST/api/v1/payscope pay:write
Amounts to pay or deduct.
Up to five hundred at a time, for a commission engine or an expenses system that decides money elsewhere. Anybody named who is not in this company is refused rather than skipped: writing into another company’s payroll is the one mistake this door cannot be allowed to make.
Parameters
transactions[].staffId- Who it is for, by their id in this company.
transactions[].label- What it is called on the payslip.
transactions[].kind- Whether it is paid or deducted.
transactions[].amount- In the company’s own currency.
transactions[].month- The month it belongs to. e.g. 2026-09
Idempotency-Key- A header. The same key replays the first answer rather than paying twice.
What comes back
written- How many were accepted.
refused- Which were not, and why.

