Payroll Leaves API
Employee leaves track absences such as sick leave, parental leave, care days, and other types of leave. The API enforces Norwegian labor law constraints, including limits on self-certified sick leave (egenmelding).
API Endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /payroll/leaves | List employee leaves |
| GET | /payroll/leaves/{id} | Get an employee leave |
| POST | /payroll/leaves | Create an employee leave |
| POST/PUT | /payroll/leaves/{id} | Update an employee leave |
| DELETE | /payroll/leaves/{id} | Cancel an employee leave |
List Employee Leaves
GET /payroll/leaves
Retrieve a paginated list of employee leaves with filtering options.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| client_account_id | integer | Yes | Filter by client account ID (must be from user’s eligible accounts) |
| business_partner_id | integer | No | Filter by employee (business partner) ID |
| status | string | No | Filter by leave status (ACTIVE, COMPLETED, CANCELLED) |
| leave_type | string | No | Filter by leave type (see Leave Types) |
| year | integer | No | Filter by calendar year (returns leaves overlapping the year) |
| page | integer | No | Page number for pagination (default: 1) |
| per_page | integer | No | Number of items per page (default: 100) |
| with | string | No | Comma-separated list of relations to include |
When filtering by year, the API returns all leaves that overlap the specified calendar year — leaves where the start date is before the end of the year and the end date is either null (ongoing) or on/after January 1 of that year.
Response
{
"data": [
{
"id": 1,
"business_partner_id": 56,
"client_account_id": 7,
"contract_id": 12,
"leave_type": "SICK_SELF_CERTIFIED",
"start_date": "2024-03-18",
"end_date": "2024-03-20",
"leave_percentage": "100.00",
"source_reference": null,
"status": "COMPLETED",
"note": "Influenza",
"created_by_id": 7,
"created_at": "2024-03-18T08:30:00Z"
}
],
"meta": {
"page": 1,
"pages": 1,
"per_page": 100,
"records": 1
}
}
Results are sorted by start_date descending (newest first).
Get Employee Leave
GET /payroll/leaves/{id}
Retrieve a single employee leave by ID.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id | integer | Leave record ID |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| with | string | No | Comma-separated list of relations to include |
Create Employee Leave
POST /payroll/leaves
Creates a new employee leave record.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| client_account_id | integer | Yes | The client account ID |
| business_partner_id | integer | Yes | The employee (business partner) ID |
| leave_type | string | Yes | Type of leave (see Leave Types) |
| start_date | date | Yes | First day of leave |
| end_date | date | No | Last day of leave (null for ongoing) |
| contract_id | integer | No | Employment contract ID |
| leave_percentage | decimal | No | Percentage of normal hours (1–100, default: 100) |
| source_reference | string | No | Reference to source document (max 100 characters) |
| status | string | No | Leave status (default: ACTIVE) |
| note | string | No | Internal notes (max 500 characters) |
Example Request
{
"client_account_id": 7,
"business_partner_id": 56,
"leave_type": "PARENTAL",
"start_date": "2024-04-01",
"end_date": "2024-06-30",
"contract_id": 12,
"leave_percentage": "50.00",
"note": "Paternity leave, 50% schedule"
}
Response
Returns the created leave with status 201 Created.
Reference Ownership
business_partner_id and contract_id are taken from the request body, so both are checked against the client account before the leave is stored:
business_partner_idmust name a business partner owned byclient_account_id.contract_id, when supplied, must name an employment contract owned byclient_account_idand belonging to the same employee asbusiness_partner_id. The employee pairing matters because A-melding and NAV refund payloads group leaves by contract — a leave on another employee’s contract would be reported under the wrong person.
Both checks fail with 400 Bad Request. An id that does not exist is rejected the same way as one owned by another client account, so the response cannot be used to probe for existing ids.
Update Employee Leave
POST/PUT /payroll/leaves/{id}
Updates an existing employee leave. Both POST and PUT are accepted. Only provided fields are updated.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id | integer | Leave record ID |
Request Body
Same fields as create, except client_account_id and business_partner_id which cannot be changed after creation.
contract_id is re-checked against the ownership rules above only when the field is present in the request body. A leave that already carries a contract you are not entitled to can therefore still be edited or cancelled without first correcting that field.
Response
Returns the updated leave.
Cancel Employee Leave
DELETE /payroll/leaves/{id}
Cancels an employee leave. This is a soft delete — the record remains in the database with status set to CANCELLED.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id | integer | Leave record ID |
Response
Returns the cancelled leave with status set to CANCELLED.
Attributes
| Attribute | Type | Description |
|---|---|---|
| id | integer | Unique identifier (read-only) |
| business_partner_id | integer | Employee (business partner) ID |
| client_account_id | integer | Client account ID |
| contract_id | integer | Employment contract ID (optional) |
| leave_type | string | Type of leave (see Leave Types) |
| start_date | date | First day of leave |
| end_date | date | Last day of leave (null if ongoing) |
| leave_percentage | decimal | Percentage of normal hours (1–100) |
| source_reference | string | Reference to source document |
| status | string | Leave status |
| note | string | Internal notes |
| created_by_id | integer | ID of the creating user (read-only) |
| created_at | datetime | Creation timestamp (read-only) |
Leave Types
| Value | Description |
|---|---|
| SICK_SELF_CERTIFIED | Self-certified sick leave (egenmelding) |
| SICK_DOCTOR_CERTIFIED | Doctor-certified sick leave |
| PARENTAL | Parental leave (foreldrepenger) |
| CARE_DAYS | Care days for sick children (omsorgsdager) |
| EDUCATION | Education or training leave |
| MILITARY | Military service leave |
| WELFARE | Welfare or social leave |
| OTHER_PAID | Other paid leave |
| OTHER_UNPAID | Other unpaid leave |
Status Values
| Value | Description |
|---|---|
| ACTIVE | Leave is currently active or scheduled |
| COMPLETED | Leave has ended |
| CANCELLED | Leave was cancelled (set by DELETE endpoint) |
Self-Certified Sick Leave Rules
Norwegian labor law imposes specific limits on self-certified sick leave (egenmelding). The API enforces these automatically:
- Maximum duration: 3 calendar days per episode, counting both the first and the last day. If
end_dateis provided it must therefore be at most 2 days afterstart_date. A leave with noend_dateis not duration-checked. - Maximum frequency: 4 episodes per calendar year. The API counts
ACTIVEandCOMPLETEDself-certified leaves whosestart_datefalls in the same calendar year as the leave being saved, excluding the record itself — so editing an existing episode does not count it twice.
Attempting to exceed these limits returns a 400 Bad Request error.
Relationships
The following related objects can be included using the with parameter:
business_partner- The employee associated with the leave
Validation Rules
business_partner_idis required, and must name a business partner in the same client accountclient_account_idis requiredcontract_id, if provided, must name an employment contract in the same client account belonging to the same employeeleave_typemust be a valid leave type valuestart_dateis requiredend_datemust be on or afterstart_date(if provided)leave_percentagemust be between 1 and 100 (if provided)statusmust be a valid status value (if provided)- Self-certified sick leave has additional duration and frequency limits (see above)
Error Responses
| Status Code | Description |
|---|---|
| 400 | Validation error (missing or invalid fields, leave limits exceeded, or a business_partner_id/contract_id that does not belong to this client account or employee) |
| 403 | Forbidden (no access to client account) |
| 404 | Leave record not found |