Leave and balances
Absences and what remains on each counter. Why someone is absent is the most sensitive data this API carries, and it is withheld unless you were granted a scope for it — see Why an absence was taken, below.
The restricted envelope used here is the one described under Withheld fields in Companies,
employees and contracts.
What a leave read returns: lines, not requests
One request can carry several lines. Three days of paid leave followed by two of RTT is one request
with two lines, and each line has its own dates, its own quantity and its own type. This API serves
lines, and requestId is what regroups those of one absence.
Without it, two lines of one request cannot be told from two requests filed on adjoining dates — and those
are not the same fact. Group on requestId before you present anything to a user.
A page boundary can separate the lines of one absence. They come out in identifier order, so side by
side — but nothing guarantees they land in the same page. Group on requestId after paging to the end,
never page by page.
Listing absences
GET /api/v1/client/leave-requests (scopes: leaves:read)
{
"items": [
{
"id": "WV5RJ7",
"requestId": "ePloqJ",
"employeeId": "GNnBNO",
"companyId": "dV3QPr",
"startDate": "2026-01-12",
"endDate": "2026-01-15",
"duration": 3.5,
"durationUnit": "Days",
"status": "Approved",
"createdOn": "<generated>",
"leaveType": null,
"isWorkStoppage": null,
"restricted": [
{
"reason": "requires_scope",
"fields": [
"leaveType",
"isWorkStoppage"
]
},
{
"reason": "requires_company_opt_in",
"fields": [
"leaveType",
"isWorkStoppage"
]
}
]
},
{
"id": "mNw9JY",
"requestId": "ePloqJ",
"employeeId": "GNnBNO",
"companyId": "dV3QPr",
"startDate": "2026-01-12",
"endDate": "2026-01-15",
"duration": 3.5,
"durationUnit": "Days",
"status": "Approved",
"createdOn": "<generated>",
"leaveType": null,
"isWorkStoppage": null,
"restricted": [
{
"reason": "requires_scope",
"fields": [
"leaveType",
"isWorkStoppage"
]
},
{
"reason": "requires_company_opt_in",
"fields": [
"leaveType",
"isWorkStoppage"
]
}
]
}
],
"nextCursor": null
}
| Parameter | What it does |
|---|---|
from, to |
ISO dates bounding the absence. Either may be omitted. |
status |
One life-cycle code, or several separated by commas. |
establishmentId |
Narrows the page to ONE establishment of your perimeter. Omitted reads all of it. |
cursor, limit |
Paging. Pass nextCursor back; stop only when it is null. |
establishmentId narrows, it never widens. Naming an establishment outside your perimeter returns
nothing rather than reaching it, and so does a value that is not an identifier this API issued — a stale
bookmark gives you an empty page, not an error and not the whole perimeter. Same parameter and same rule on
the employee listing.
The window matches by overlap, not containment. An absence running from 28 January to 3 February is returned for January and for February. That is what you want when closing a payroll month: asking for absences wholly inside the month would drop exactly the ones that straddle the closure.
An unparseable date leaves that side of the window open rather than failing the call. You may receive more than you asked for, never less.
A granted absence is not one status but three
An absence that has been granted enters Approved, becomes Treated once payroll has taken it, then
Terminated. Filtering on Approved alone loses absences exactly as they become certain. For a planning
view, ask for all three:
GET /api/v1/client/leave-requests?from=2026-01-01&to=2026-01-31&status=Approved,Treated,Terminated
The full set is SentForUpdate, ApprobationPending, Approved, Refused, TreatmentPending, Treated,
Terminated, AbortionPending, Aborted, Imported. Unsubmitted drafts are never served: they are an
employee's unsent intention, visible to nobody else in Staff & Go either.
A line that disappears is absent, not withdrawn
There is no delta feed and no tombstone. A request cancelled after you read it simply stops being returned — nothing announces it. Re-read the window rather than waiting for a deletion event, and do not treat a line you once saw as still active because no event said otherwise.
One absence
GET /api/v1/client/leave-requests/{id} (scopes: leaves:read)
Takes the id of a line, as returned by the listing.
Quantities always travel with their unit
| Field | What it tells you |
|---|---|
duration |
How much absence the line is, half-days included — an afternoon off is 0.5. |
durationUnit |
What that number counts: Days or Hours. |
duration alone is meaningless. The same 7 is seven days or seven hours depending on the company's
configuration for that leave type. Reading the number without the unit puts you wrong by roughly the length
of a working day, in whichever direction you guessed. Never store or display one without the other.
Why an absence was taken
leaveType and isWorkStoppage sit behind leaves:read:reason, required in addition to
leaves:read, which never implies it.
A leave type discloses sick or maternity leave, which is health data under Article 9 GDPR — the most sensitive category this API touches. So the default answer says when and how much, never why. A planning integration needs the dates; only an integration with a stated need should ever hold this scope.
The two fields are released together and withheld together. A partner holding only the work-stoppage flag could still separate sickness from holiday, so releasing either one alone would disclose what the pair exists to protect.
isWorkStoppage: null means withheld. It never means false. Reading it as false would state that the
absence is not a work stoppage — a claim about someone's health, rather than the absence of one. Check
restricted before you interpret it.
Balances
GET /api/v1/client/employees/{id}/leave-balances (scopes: leaves:read)
{
"items": [
{
"employeeId": "GNnBNO",
"companyId": "1VaKPa",
"counterCode": "CP",
"counterName": "Cong\u00E9s pay\u00E9s",
"unit": "Days",
"acquired": 25,
"taken": 10,
"remaining": 14.5
},
{
"employeeId": "GNnBNO",
"companyId": "1VaKPa",
"counterCode": "RTT",
"counterName": "RTT",
"unit": "Days",
"acquired": 12,
"taken": 12,
"remaining": 0
}
]
}
| Field | What it tells you |
|---|---|
employeeId |
Whose balance it is. |
companyId |
The establishment they belong to — the opaque identifier /companies serves. Always present: an employee file belongs to exactly one. |
counterCode |
Stable across companies (CP, RTT) — compare on this, not on counterName. |
counterName |
A label, in your language. The company can rename it. |
unit |
Days or Hours. |
acquired, taken, remaining |
Accrued, consumed, left. |
Never recompute remaining as acquired − taken. The three are maintained independently by the
accrual engine: a counter that rolls over from another one, or one carrying a manual adjustment, does not
satisfy that subtraction. Recomputing it makes you disagree with what the employee sees in Staff & Go, for
exactly those employees. Read remaining as served.
Unpaged — a company has a handful of counters, and they all come back at once. There is no restricted
envelope on this read: a balance says how much is left, never why an absence was taken, so there is nothing
here to withhold.
An employee id that is not one of yours answers 404, not an empty list.