Companies that use external systems (i.e., different than shyftplan) to manage time accounts have the option to integrate those time accounts that are relevant for shift planning purposes with shyftplan. For this, shyftplan features a specific absence reason "time account" against which absences can be taken and which can be addressed via API endpoints.
This article provides the relevant background for external time accounts and explains how to set up, sync and use external time accounts, structured in 6 sections:
- High-level overview
- Synchronisation requirements for external "current balance" vs. automatically calculated "projected balance"
- Calculation logic
- Creating/Updating of the absence reason
- Creating/Updating time-account absences
- Updating employees' balances
1. High-level overview
To view and use the balance (e.g. accumulated overtime) from an external time account in shyftplan, you first need to create an appropriate absence reason for it in shyftplan.
This absence reason has to be of the type "time account" and can then be used in shyftplan as the base for absences against the time account.
👉The absence reason can only be created, and the balances synced, by way of our public API, which is documented at www.developer.shyftplan.com.
On creation of the absence reason, an employment time account for each current and future employment is automatically created and its lower/upper warnings/limits set to the absence reason's default values.
Thereafter, an employee's time account limits, warnings and also the balance are configurable.
2. Synchronisation requirements for external "current balance" vs. automatically calculated "projected balance"
When an employee looks at his balance for a particular time account (or a manager does the same for a time account of an employee), shyftplan shows him the value of the "balance" property as per the last sync. The sync process has to make sure that the correct balance is present at the time of viewing. The balance will be displayed "as is" and shyftplan doesn't perform deductions from or additions to it, thereby assuming that all (past) absences of the employee against the time account have already been taken into account.
Additionally the "projected balance" is shown. This is calculated in shyftplan by deducting future, approved absences (against the time account) from the current balance. The calculation is done following the settings for the time account absence reason by default, but you can also configure individual absences - see "Calculation logic". Future absences include all of today's absence hours, tomorrow’s absence hours, and so on.
The recommended sync process ensures that the current balance is updated regularly to show the value of the external time account as per the start of a day.
An example should make this clearer - we will use full hours here (instead of minutes as used by the API) for clarity: Let's assume an employee takes an absence for Wednesday, Thursday and Friday, and a manager approves it.
According to either the preset calculation logic of the time account absence reason, or because the absence was later re-configured (see "Calculation logic" further down), shyftplan stores a value for the absence hours of every day of the example absence as follows:
Let's further assume that today is Monday (first day in the table above), and the current time account balance is at +20 hours. The employee will look at his time account through shyftplan and see this current balance (+20:00) and also the projected balance, which will deduct all future absences from the current balance:
projected balance = 20 - 8 - 0 - 8 = 4:00
On Monday morning, the employee sees these values:
The current time account balance will not change and always stay at +20:00, until a sync process updates it to a different value. In contrast, the projected balance will change as time passes, because, on Thursday morning, the absence hours from Wednesday are no longer part of the future absences. Only 8 hours from Friday remain to be deducted from whichever current balance value has been set at the latest sync.
At the start of Thursday, the external system must be updated and the time account balance synced to shyftplan. It must be updated to a value of 12 hours to allow shyftplan a correct calculation of the projected balance:
projected balance = 12 - 0 - 8 = 4
Thus, on Thursday (and also on Friday), the employee sees these values:
The next update is necessary on Saturday, when no future absence hours remain and the current balance should reflect the past absence. This assumes an update in the external system has happened, by which the absence from Friday was deducted from the external current balance and the new external current balance of 4 was synced to shyftplan.
3. Calculation logic
The absence reason that is created in shyftplan for the external time account must be configured on how to value its absences by default, i.e. how much time will be deducted from a time account because of each individual day of absence. For this deduction, shyftplan uses the summed-up values of all future days of absence, deducts this value from the current balance, and displays it as the projected balance.
The individual absence against any time-account (actually even regarding any available absence reason) can be set to different hours per day from the preset calculation logic. See further below for endpoints.
The calculation differs for full-day versus part-day absences. This table shows the different calculation types and how they work:
Calculation type / option name |
Description |
planned_shifts_ |
If an employee has a planned shift on a given day, the deduction is calculated according to "planned_shifts" below. However, if there is no planned shift, the deduction reverts to the calculation method according to "rotations" below |
Full day absences |
If the employee has planned shifts, the deduction follows the "planned shifts" logic
|
Partial day absences |
|
rotations |
If an employee has a shift as per their rotation pattern on a given day, the deduction is calculated by subtracting the break time from the shift duration. If the shift is not part of the rotation, then it will not be considered |
Full day absences |
|
Partial day absences |
|
planned_shifts |
If an employee has a planned shift on a particular day, the deduction equals the working hours of the shift minus any break times |
Full day absences |
|
Partial day absences |
|
employee_working_ |
On working days, the deduction is the 'Hours per (paid) day of absence’ in the employee's profile. On non-working days, no hours are deducted |
Full day absences |
|
Partial day absences |
|
4. Create/Update/Read the time-account absence reason
These endpoints can be used to create absence reasons for time accounts, to change and to list them, and to retrieve accumulated data about them:
A. POST https://shyftplan.com/v2/absence_reasonsB. PATCH https://shyftplancom/v2/absence_reasons/{id}
C. GET https://shyftplan.com/v1/absence_reasons and GET …v1/absence_reasons/{id}
D. GET https://shyftplan.com/v1/absences/stats
The endpoints will be documented on developer.shyftplan.com
A. POST absence_reasons
POST https://shyftplan.com/v2/absence_reasons
Use this endpoint to create an absence reason for absences against a time-account.
This table shows the available parameters/properties in the payload, their meaning and the options for their values:
Property |
Description |
Option/Type of value |
---|---|---|
user_email |
E-Mail of the API user |
(String) (required) |
authentication_ |
Token for API access |
(String) (required) |
company_id |
ID of the company's shyftplan account |
(Int) (required) |
type |
Type of absence reason, one of "with_entitlement", "without_entitlement", "time account". The latter must be used for absence reasons related to external time accounts |
time_account (required) |
name |
The name to be used for the new absence reason in shyftplan |
(String) (required) |
calculation_type |
One of several string values to determine the mode of calculation for the "effective duration" of an absence. For time-accounts, use one of the values on the right. For time-accounts, those function as default calculation types that can be overridden in individual absences. |
One of: "planned_shifts_ (required) |
short_name |
Abbreviation for the absence reason |
(String) (max. 3 letters) (optional) |
employee_ |
When set to "true", employees can use the absence reason to request absences. Conversely, when set to "false", only managers have the ability to use the absence reason on behalf of employees |
Boolean (optional, default: true) |
lower_limit_ |
Default lower limit for an employee's current and projected balance in minutes - negative numbers allowed |
(Int) (optional, default: null) |
lower_warning_ |
Default lower warning for employment time accounts. Warn employees/managers when the current/projected balance is lower than this value - negative numbers allowed |
(Int) (optional, default: null) |
upper_warning_ |
Default upper warning for employment time accounts. Warn employees/managers when the current/projected balance is higher than this value - negative numbers allowed |
(Int) (optional, default: null) |
upper_limit_ |
Default upper limit for an employee's current and projected balance in minutes - negative numbers allowed |
(Int) (optional, default: null) |
An example curl request looks like this
curl -X 'POST' \
'https://shyftplan.com/api/v2/absence_reasons' \
-H 'accept: application/json' \
-H 'Content-Type: application/x-www-form-urlencoded;' \
-d 'user_email=api_user%40my.corp&authentication_token=tkn54321&company_id=12345&type=time_account&name=External%20Time%20Account&calculation_type=rotations&short_name=ETA&employee_management_enabled=true&lower_limit_minutes=-30&lower_warning_minutes=-15&upper_warning_minutes=%2B75&upper_limit_minutes=%2B100&calculation_type=rotations'
The response looks like this:
{
"id": 103905,
"company_id": 12136,
"name": "Absence reason",
"type": "with_entitlement",
"has_localization": false,
"deleted_at": null,
"created_at": "2024-05-31T10:23:25.230+02:00",
"updated_at": "2024-05-31T10:23:25.230+02:00",
"is_absence_attachments_allowed": false,
"days": 1,
"calculation_type": "standard",
"carry_over_days_enabled": false,
"carry_over_days_deadline": null,
"hours_calculation_type": "employee_profile",
"short_name": null,
"is_default": false,
"lower_limit_minutes": null,
"lower_warning_minutes": null,
"upper_warning_minutes": null,
"upper_limit_minutes": null,
}
B. PATCH absence_reasons/{id}
PATCH https://shyftplan.com/v2/absence_reasons/{id}
{id} - replace with the ID of the absence reason, e.g. 2452221
Use this endpoint to change/update an absence reason for absences against a time-account.
This table shows the available parameters/properties in the payload, their meaning and the options for their values. Only parameters relevant for time-account absence reasons are shown.
Property |
Description |
Option/Type of value |
---|---|---|
user_email |
E-Mail of the API user |
(String) (required) |
authentication_ |
Token for API access |
(String) (required) |
company_id |
ID of the company's shyftplan account |
(Int) (required) |
name |
The new name to be used for the absence reason in shyftplan |
(String) (optional) |
calculation_type |
One of several string values to determine the mode of calculation for the "effective duration" of an absence. For time-accounts, use one of the values on the right if you want to change the calculation type. Change will only apply to new absences or, when changing the range of an existing absence, to additional dates from the new range. For time-accounts, those function as default calculation types that can be overridden in individual absences. |
(String) (optional) One of "planned_shifts_ |
short_name |
Abbreviation for the absence reason |
(String) (max 3 letters) (optional) |
employee_ |
When set to "true", employees can use the absence reason to request absences. Conversely, when set to "false", only managers have the ability to use the absence reason on behalf of employees |
Boolean (optional) |
lower_limit_ |
Default lower limit for an employee's current and projected balance in minutes - negative numbers allowed |
(Int) (optional) |
lower_warning_ |
Default lower warning for employment time accounts. Warn employees/managers when the current/projected balance is lower than this value - negative numbers allowed |
(Int) (optional) |
upper_warning_ |
Default upper warning for employment time accounts. Warn employees/managers when the current/projected balance is higher than this value - negative numbers allowed |
(Int) (optional) |
upper_limit_ |
Default upper limit for an employee's current and projected balance in minutes - negative numbers allowed |
(Int) (optional) |
The response looks like this:
{
"id": 103905,
"company_id": 12136,
"name": "Absence reason",
"type": "with_entitlement",
"has_localization": false,
"deleted_at": null,
"created_at": "2024-05-31T10:23:25.230+02:00",
"updated_at": "2024-05-31T10:23:25.230+02:00",
"is_absence_attachments_allowed": false,
"days": 1,
"calculation_type": "standard",
"carry_over_days_enabled": false,
"carry_over_days_deadline": null,
"hours_calculation_type": "employee_profile",
"short_name": null,
"is_default": false,
"lower_limit_minutes": null,
"lower_warning_minutes": null,
"upper_warning_minutes": null,
"upper_limit_minutes": null,
}
C. GET absence_reasons and …/{id}
GET https://shyftplan.com/api/v1/absence_reasons/{id}
{id} - replace with the ID of an absence reason
Use this endpoint to get details about a single absence reason. This endpoint is already well-documented on developer.shyftplan.com.
GET https://shyftplan.com/api/v1/absence_reasons
Use this endpoint to retrieve a list of available absence reasons. This endpoint is already well-documented on developer.shyftplan.com. In a recent expansion, the filter parameter types has been added:
Property |
Description |
Option/Type of value |
---|---|---|
… |
(see endpoint documentation on developer.shyftplan.com) |
|
types |
Filter the list of absence reasons to only include the given absence reason types |
(Array of String) (optional) Options for String: "with_entitlement", "without_entitlement", "time_account" |
The response for GET …/absence_reasons/{id} looks like this:
{
"id": 532784,
"type": "time_account",
"company_id": 12345,
"name": "Overtime",
"short_name": "OTA",
"days": null,
"carry_over_days_enabled": false,
"carry_over_days_deadline": null,
"calculation_type": "rotations",
"hours_calculation_type": null,
"is_absence_attachments_allowed": false,
"lower_limit_minutes": -60,
"lower_warning_minutes": 0,
"upper_warning_minutes": 600,
"upper_limit_minutes": 1200,
"employee_management_enabled": true,
"created_at": "2024-05-31T10:23:25.230+02:00",
"updated_at": "2024-05-31T10:23:25.230+02:00"
}
For GET …/absence_reasons, the response payload will be a JSON object with a property "items", which holds an array of objects like the above.
D. GET absences/stats
GET htttps://shyftplan.com/api/v1/absences/stats
Use this endpoint to get accumulated balances for time-account absence reasons (and also for other types of absence reasons).
This table shows the available parameters/properties in the payload, their meaning and the options for their values.
Property |
Description |
Option/Type of value |
---|---|---|
user_email |
E-Mail of the API user |
(String) (required) |
authentication_ |
Token for API access |
(String) (required) |
company_id |
ID of the company's shyftplan account |
(Int) (required) |
starts_at |
Limit the interval for absences taken into account Example value: "2024-05-31T10:23:25.230+02:00" |
(DateTime) (optional) |
ends_at |
Limit the interval for absences taken into account Example value: "2024-05-31T10:23:25.230+02:00" |
(DateTime) (optional) |
employment_ids[] |
Only take employments with these IDs into account. If this is left empty, all employments are included |
(Array of Int) (optional) |
location_ids[] |
Only take an absence into account, when the employment can work in a location the ID of which is included in the array. If this is left empty, all locations are included |
(Array of Int) (optional) |
employee_ |
When set to "true", employees can use the absence reason to request absences. Conversely, when set to "false", only managers have the ability to use the absence reason on behalf of employees |
Boolean (optional) |
attachment |
"true": Only include absences for which an attachment was uploaded. "false": Only include absences without attachment. (Empty): Include both. |
(Boolean) (optional) |
The response looks like this: (type of value shown instead of value)
{
"absence_reasons": [
{
"id": (Integer),
"name": (String),
"type": (String),
"limit": (Integer), // present only if filtering for one employee
"used": (Integer),
"carry_over_info": {
"used": (Integer),
"limit": (Integer)
}, // present only if filtering for one employee
"balance_minutes": (Integer)
}
],
"total_days": (Integer)
}
5. Create/Update/Read an absence against a time-account
Use one of these endpoints:
A. POST https://shyftplan.com/api/v1/absences
B. PUT https://shyftplan.com/api/v1/absences
C. GET https://shyftplan.com/api/v1/absences and …/absences/{id}
A. POST api/v1/absences
POST https://shyftplan.com/api/v1/absences
Use this endpoint to create an absence against an external time-account
Using the endpoint will not perform a sync with the external time-account. Instead, an absence in shyftplan will be created using the details from the POST request, with a reference to an absence reason, which is of type "time_account". The employment's balance stored there will not change, but the projected balance calculated by shyftplan and shown in the GUI will change,
- if the absence lies (at least partly) in the future
- and if the absence's state is "accepted".
The integration code is in charge of updating the balance so that this value displays the correct status/time amount in shyftplan every day.
This table shows the available parameters/properties in the payload, their meaning and the options for their values, but only for absences against external time-accounts:
Property |
Description |
Option/Type of value |
---|---|---|
user_email |
E-Mail of the API user |
(String) (required) |
authentication_ |
Token for API access |
(String) (required) |
company_id |
ID of the company's shyftplan account |
(Int) (required) |
employment_id |
ID of the employment that the absence applies to, as stored in the shyftplan database |
(Int) (required) |
absence_reason_id |
ID of the absence reason in shyftplan. For an absence against an external time account this needs to be the ID of a reason of type "time_account" |
(Int) (required) |
starts_at |
Start of the absence. For full-day absences, only the date part matters. E.g. "2024-01-01T09:05:03.321Z" |
(DateTime) (required) |
ends_at |
End of the absence. For full-day absences, only the date part matters. E.g. "2024-01-01T09:05:03.321Z" |
(DateTime) (required) |
days |
(can be any value of type Float, e.g. "0" - will be disregarded but most be present in the request) |
(Float) (required) |
paid |
Must be set to "true" for time-account absences. |
(Boolean) (required) |
state |
State of the absence |
(String) (optional, default: "new") One of "new", "accepted", "refused", "withdrawn" |
notes |
Comment to be stored with the absence |
(String) (optional) |
is_full_day |
Is this an absence for entire days only? Set this to "false" for an absence that starts or ends at a certain time on a date |
(Boolean) (optional, default: true) |
days_breakdown |
Instead of the hour amounts calculated from the "calculation_type" of the absence_reason, set other values for each day of the absence |
(Array of Hash) (optional) - see below this table - |
The days_breakdown array consists of JSON objects for each day of the absence. Each object looks like this:
{
"day": Date, // e.g. "2024-06-30"
"minutes": Int // e.g. 240
}
Both fields are required if days_breakdown is used, but days_breakdown is an optional parameter
The response for the successful POST request is
{
"id": 137640,
"starts_at": "2024-07-24T00:00:00.000+02:00",
"ends_at": "2024-07-26T00:00:00.000+02:00",
"state": "new",
"reason": null,
"notes": null,
"employment_id": 908066,
"created_at": "2024-06-20T17:55:55.458+02:00",
"updated_at": "2024-06-20T17:55:55.458+02:00",
"deleted_at": null,
"days": 1.0,
"vacation_minutes": null,
"paid": false,
"absence_reason_id": 104454,
"is_full_day": true,
"refuse_message": null,
"metadata": {},
"file_uploaded_at": null,
"state_updated_at": null,
"file_name": null,
"file_uid": null,
"file": null,
"deleted_staff_shifts_info": null,
"can_manage": true
}
B. PUT absences
PUT https://shyftplan.com/api/v1/absences
Use this endpoint to update an absence against an external time-account
Using the endpoint will not perform a sync with the external time-account. Instead, an absence in shyftplan will be updated using the details from the PUT request, with a reference to an absence reason, which is of type "time_account". The employment's balance stored there will not change, but the projected balance calculated by shyftplan and shown in the GUI will change,
- if the absence lies (at least partly) in the future
- and if the absence's state is "accepted".
The integration code is in charge of updating the balance so that this value displays the correct status/time amount in shyftplan every day.
This table shows the available parameters/properties in the payload, their meaning and the options for their values, but only for absences against external time-accounts:
Property |
Description |
Option/Type of value |
---|---|---|
user_email |
E-Mail of the API user |
(String) (required) |
authentication_ |
Token for API access |
(String) (required) |
company_id |
ID of the company's shyftplan account |
(Int) (required) |
id |
ID of the absence as stored in the shyftplan database |
(Int) (required) |
absence_reason_id |
ID of the absence reason in shyftplan. Can only be changed to an ID of another absence reason of type "time_account" |
(Int) (optional) |
starts_at |
Start of the absence. For full-day absences, only the date part matters. E.g. "2024-01-01T09:05:03.321Z" |
(DateTime) (optional) |
ends_at |
End of the absence. For full-day absences, only the date part matters. E.g. "2024-01-01T09:05:03.321Z" |
(DateTime) (optional) |
refuse_message |
Comment to be stored with the absence detailing a reason for a refusal of an absence request |
(String) (optional) |
state |
State of the absence |
(String) (optional) One of "new", "accepted", "refused", "withdrawn" |
notes |
Comment to be stored with the absence |
(String) (optional) |
is_full_day |
Is this an absence for entire days only? Set this to "false" for an absence that starts or ends at a certain time on a date |
(Boolean) (optional, default: true) |
days_breakdown |
Instead of the hour amounts calculated from the "calculation_type" of the absence_reason, set other values for each day |
(Array of Hash) (optional) - see below this table - |
The days_breakdown array consists of JSON objects for each day of the absence. Each object looks like this:
{
"day": Date, // e.g. "2024-06-30"
"minutes": Int // e.g. 240
}
Both fields are required if days_breakdown is used, but days_breakdown is an optional parameter
The response for the successful PUT request is the same as for POST
C. GET absences and absences/{id}
- GET https://shyftplan.com/api/v1/absences - for a list of many absences
- GET https://shyftplan.com/api/v1/absences/{id} - for a single absence
{id} - replace with the ID of an absence as stored in the shyftplan database
These GET endpoints are already documented on developer.shyftplan.com. The response will look like this: (Type shown instead of value):
{
"id": (Integer),
"absence_reason_type": (String), // "time_account"
"starts_at": (DateTime),
"ends_at": (DateTime),
"state": (String),
"state_updated_at": (DateTime),
"notes": (String),
"employment_id": (Integer),
"created_at": (DateTime),
"updated_at": (DateTime),
"deleted_at": (DateTime),
"days": (Float),
"vacation_minutes": (Integer),
"paid": (Boolean),
"absence_reason_id": (Integer),
"is_full_day": (Boolean),
"refuse_message": (String),
"metadata": (JSON),
"file_uid": (Integer),
"file_name": (String),
"file_uploaded_at": (DateTime),
"can_manage": (Boolean), //
"file": (String),
"absence_days": (Array[AbsenceDay])
}
Absence Day will be a JSON object for each day of the absence like this (pseudo-code with included comments):
{
"day": (Date), // YYYY-MM-DD
"minutes": (Integer), // absence time for date, see POST or PUT above
"original_value": (Integer), // The value calculated based off of the
// calculation type of the absence reason
"special_day": (String), // name of the special day, if any
"last_edited_by": (Integer) // ID of last employment to edit the object
}
6. Update/Read employees' balances
Upon creation of a time-account absence reason (see previous section), employees' time account balances as well as warnings and limits can be updated (PATCH) or read (GET) via the two endpoints
A. PATCH https://shyftplan.com/api/v1/employment_time_accounts
B. GET https://shyftplan.com/api/v1/employment_time_accounts
Both endpoints will be documented on developer.shyftplan.com, where the technical details following can also be found.
A. PATCH
PATCH https://shyftplan.com/api/v1/employment_time_accounts
Use this endpoint to change an employee's time account details.
This table shows the available parameters/properties in the payload, their meaning and the options for their values:
Property |
Description |
Option/Type of value |
---|---|---|
user_email |
E-Mail of the API user |
(String) (required) |
authentication_ |
Token for API access |
(String) (required) |
company_id |
ID of the company's shyftplan account |
(Int) (required) |
employment_id |
ID of the employee as stored in the shyftplan database |
(Int) (required) |
absence_reason_id |
ID of the absence reason used for the external time account as stored in the shyftplan database |
(Int) (required) |
lower_limit_ |
Lower limit for the current/projected balance |
(Int) (optional) |
lower_warning_ |
When the current/projected balance drops below this threshold, a warning will be shown in the GUI |
(Int) (optional) |
upper_warning_ |
When the current/projected balance surpasses this threshold, a warning will be shown in the GUI |
(Int) (optional) |
upper_limit_ |
Lower limit for the current/projected balance |
(Int) (optional) |
balance_minutes |
Value to be shown as current balance to the employee and to managers in the shyftplan GUI |
(Int) (optional) |
An example CURL request looks like this:
curl -X 'PATCH' \
'https://shyftplan.com/api/v1/employment_time_accounts' \
-H 'accept: application/json' \
-H 'Content-Type: application/json;' \
-d '{
"user_email": "api_user@my.corp",
"authentication_token": "tkn54321",
"company_id": 12345,
"employment_id": 54432,
"absence_reason_id": 11223,
"balance_minutes": 1200,
"lower_limit_minutes": -1200,
"lower_warning_minutes": -600,
"upper_warning_minutes": 1200,
"upper_limit_minutes": 2400
}'
The response looks like this:
{
"id": 7889332,
"employment_id": 54432,
"absence_reason_id": 11223,
"balance_minutes": 1200,
"upper_limit_minutes": 2400,
"upper_warning_minutes": 1200,
"lower_warning_minutes": -600,
"lower_limit_minutes": -1200,
"created_at": "2024-01-01T09:05:03.321+01:00",
"updated_at": "2024-05-03T09:10:02.148+02:00"
}
B. GET
GET https://shyftplan.com/api/v1/employment_time_accounts
Use this endpoint to get a (filtered) list of employee's time account details.
This table shows the available parameters/properties in the payload, their meaning and the options for their values:
Property |
Description |
Option/Type of value |
---|---|---|
user_email |
E-Mail of the API user |
(String) (required) |
authentication_ |
Token for API access |
(String) (required) |
company_id |
ID of the company's shyftplan account |
(Int) (required) |
employment_ids |
Filter the list returned to only include employees with given IDs |
(Array of Int) (optional) |
absence_reason_ |
Filter the list returned to only include absence reasons with given IDs. Each ID must belong to an absence reason of type "time_account" |
(Array of Int) (optional) |
An example CURL request looks like this:
curl --request GET \
--url 'https://shyftplan.com/api/v1/employment_time_accounts?user_email=api_user%40my.corp&authentication_token=tkn54321&company_id=12345&employment_ids[]=11223&employment_ids[]=12435&absence_reason_ids[]=72873&absence_reason_ids[]=63746' \
--header 'accept: application/json'
The response looks like this:
{
"items": [
{
"id": 1234567,
"employment_id": 11223,
"absence_reason_id": 72873,
"balance_minutes": 60,
"upper_limit_minutes": 2400,
"upper_warning_minutes": 1200,
"lower_warning_minutes": -1200,
"lower_limit_minutes": -2400,
"created_at": "2024-01-01T09:05:01.123+01:00",
"updated_at": "2024-05-31T09:15:02.345+02:00"
},
{
"id": 1234567,
"employment_id": 11223,
"absence_reason_id": 63746',
"balance_minutes": 120,
"upper_limit_minutes": 2400,
"upper_warning_minutes": 1200,
"lower_warning_minutes": -1200,
"lower_limit_minutes": -2400,
"created_at": "2024-01-01T09:05:01.123+01:00",
"updated_at": "2024-05-31T09:15:02.345+02:00"
},
{
"id": 1234567,
"employment_id": 12435,
"absence_reason_id": 72873,
"balance_minutes": 0,
"upper_limit_minutes": 2400,
"upper_warning_minutes": 1200,
"lower_warning_minutes": -1200,
"lower_limit_minutes": -2400,
"created_at": "2024-01-01T09:05:01.123+01:00",
"updated_at": "2024-05-31T09:15:02.345+02:00"
},
{
"id": 1234567,
"employment_id": 12435,
"absence_reason_id": 63746',
"balance_minutes": -60,
"upper_limit_minutes": 2400,
"upper_warning_minutes": 1200,
"lower_warning_minutes": -1200,
"lower_limit_minutes": -2400,
"created_at": "2024-01-01T09:05:01.123+01:00",
"updated_at": "2024-05-31T09:15:02.345+02:00"
}
],
"total": 4
}