Payroll Contracts API

Employment contracts represent the formal employment agreements between a company and its employees. Each contract captures salary details, working arrangements, pension configuration, and is used as the basis for payroll runs and A-melding reporting to Norwegian tax authorities.

API Endpoints

Method Endpoint Description
GET /payroll/contracts List employment contracts
GET /payroll/contracts/{id} Get an employment contract
POST /payroll/contracts Create an employment contract
POST/PUT /payroll/contracts/{id} Update an employment contract

List Employment Contracts

GET /payroll/contracts

Retrieve a paginated list of employment contracts 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
is_active boolean No Filter by active status
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

Response

{
  "data": [
    {
      "id": 1,
      "business_partner_id": 56,
      "client_account_id": 7,
      "employment_type": "EMPLOYED",
      "start_date": "2024-01-15",
      "end_date": null,
      "job_title": "Accountant",
      "occupation_code_lv": "2511102",
      "employment_form": "PERMANENT",
      "work_time_arrangement_lv": "ikkeSkift",
      "work_percentage": "100.00",
      "weekly_hours": "37.50",
      "salary_type": "MONTHLY_FIXED",
      "monthly_salary": "45000.000000",
      "hourly_rate": null,
      "vacation_pay_rate": "0.1200",
      "pension_rate": "0.0200",
      "employee_pension_rate": "0.0200",
      "pension_provider_id": null,
      "union_dues_amount": null,
      "car_registration_number": null,
      "car_list_price": null,
      "is_primary_employer": true,
      "is_active": true,
      "is_payroll_default": true,
      "arbeidsforhold_id": "a1b2c3d4e5f6g7h8i9j0",
      "created_by_id": 7,
      "created_at": "2024-01-15T10:00:00Z"
    }
  ],
  "meta": {
    "page": 1,
    "pages": 1,
    "per_page": 100,
    "records": 1
  }
}

Get Employment Contract

GET /payroll/contracts/{id}

Retrieve a single employment contract by ID.

Path Parameters

Parameter Type Description
id integer Contract ID

Query Parameters

Parameter Type Required Description
with string No Comma-separated list of relations to include

Create Employment Contract

POST /payroll/contracts

Creates a new employment contract. Automatically triggers a background task to fetch tax cards from Skatteetaten for the current tax year.

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
employment_type string Yes EMPLOYED, MARITIME, FREELANCER or PENSIONER
start_date date Yes Employment start date
salary_type string Yes MONTHLY_FIXED, HOURLY or COMMISSION (no base pay; each run gets COMMISSION lines)
monthly_salary decimal Conditional Required when salary_type is MONTHLY_FIXED
hourly_rate decimal Conditional Required when salary_type is HOURLY
end_date date No Employment end date
end_date_reason string No Reason for end date (max 50 characters)
job_title string No Job title (max 200 characters)
occupation_code_lv string No 7-digit occupation code from SSB’s yrkeskatalog, stored verbatim
employment_form string No PERMANENT or TEMPORARY
work_time_arrangement_lv string No Skatteetaten’s arbeidstidsordning code, stored verbatim (see below)
work_percentage decimal No Agreed position in percent, 0–100 (e.g. 100.00 for full-time)
weekly_hours decimal No Hours per week in a full-time position at the workplace (normally 37.5), not the employee’s own hours; above 0 and at most 168
remuneration_type string No Remuneration type (max 50 characters); not reported in the a-melding, which dropped avloenningstype in 2019
last_salary_change_date date No Date of last salary change
last_work_pct_change_date date No Date of last work percentage change
vacation_pay_rate decimal No Vacation pay rate as a fraction (e.g. 0.1200 for 12%)
pension_rate decimal No Employer pension rate as a fraction (e.g. 0.0200 for 2%)
employee_pension_rate decimal No Employee pension rate as a fraction
pension_provider_id integer No Pension provider business partner ID
union_dues_amount decimal No Monthly union dues amount
car_registration_number string No Vehicle registration number for company car benefit (max 20 characters)
car_list_price decimal No List price of company car (used for a-melding reporting to Skatteetaten)
is_primary_employer boolean No Whether this is the primary employer (default: true)
is_active boolean No Whether the contract is active (default: true)
is_payroll_default boolean No Whether this is the default contract for payroll (default: false)

Example Request

{
  "client_account_id": 7,
  "business_partner_id": 56,
  "employment_type": "EMPLOYED",
  "start_date": "2024-01-15",
  "salary_type": "MONTHLY_FIXED",
  "monthly_salary": "45000.00",
  "job_title": "Accountant",
  "occupation_code_lv": "2511102",
  "employment_form": "PERMANENT",
  "work_time_arrangement_lv": "ikkeSkift",
  "work_percentage": "100.00",
  "weekly_hours": "37.50",
  "vacation_pay_rate": "0.1200",
  "pension_rate": "0.0200",
  "is_primary_employer": true,
  "is_payroll_default": true
}

Local values

Fields ending in _lv hold a code from a national code list exactly as the authority writes it, rather than a Snapbooks enum:

  • occupation_code_lv: the 7-digit code from SSB’s yrkeskatalog, e.g. 2511102.
  • work_time_arrangement_lv: one of Skatteetaten’s arbeidstidsordning codes: ikkeSkift, andreSkift, skift365, doegnkontinuerligSkiftOgTurnus355, helkontinuerligSkiftOgAndreOrdninger336 or offshore336.

When a create or update request includes job_title for an EMPLOYED contract that has no occupation_code_lv, Snapbooks looks the code up from the title, the company’s industry and its size before saving. This adds a few seconds to that request. A code that is already set is never replaced; to look it up again, send occupation_code_lv: null together with job_title. If no code fits, the contract is saved without one.

An EMPLOYED contract is reported to Skatteetaten as an ordinary arbeidsforhold, which needs occupation_code_lv, work_percentage, weekly_hours, work_time_arrangement_lv, employment_form and last_salary_change_date. Without them the a-melding is accepted but flagged for correction.

Response

Returns the created employment contract with status 201 Created.

Reference Ownership

business_partner_id and pension_provider_id are taken from the request body, so both are checked against the client account before the contract is stored. Each must name a business partner owned by client_account_id; anything else fails 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 Employment Contract

POST/PUT /payroll/contracts/{id}

Updates an existing employment contract. Both POST and PUT methods are accepted. Only provided fields are updated.

Path Parameters

Parameter Type Description
id integer Contract ID

Request Body

Same fields as create, except client_account_id and business_partner_id which cannot be changed after creation.

pension_provider_id is re-checked against the ownership rule above only when the field is present in the request body. A contract that already carries a pension provider you are not entitled to can therefore still be edited or deactivated without first correcting that field.

Side Effects

  • Setting is_active from false to true triggers a background task to fetch tax cards.
  • Setting is_active from true to false triggers an A-melding submission to Skatteetaten with the end date.

Response

Returns the updated employment contract.

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
employment_type string EMPLOYED, MARITIME, FREELANCER or PENSIONER
start_date date Employment start date
end_date date Employment end date (null if ongoing)
end_date_reason string Reason for end date
job_title string Job title
occupation_code_lv string SSB occupation code (local value)
occupation_name string SSB’s name for the occupation code (read-only)
employment_form string PERMANENT or TEMPORARY
work_time_arrangement_lv string Skatteetaten arbeidstidsordning code (local value)
work_percentage decimal Work percentage (e.g. 100.00)
weekly_hours decimal Weekly working hours
remuneration_type string Remuneration type
salary_type string MONTHLY_FIXED, HOURLY or COMMISSION
last_salary_change_date date Date of last salary change
last_work_pct_change_date date Date of last work percentage change
monthly_salary decimal Monthly salary amount
hourly_rate decimal Hourly rate amount
vacation_pay_rate decimal Vacation pay rate
pension_rate decimal Employer pension rate
employee_pension_rate decimal Employee pension rate
pension_provider_id integer Pension provider business partner ID
union_dues_amount decimal Monthly union dues amount
car_registration_number string Vehicle registration number for company car benefit
car_list_price decimal List price of company car (used for a-melding bil tilleggsinformasjon)
arbeidsforhold_id string Employment relationship ID (auto-generated UUID)
is_primary_employer boolean Whether this is the primary employer
is_active boolean Whether the contract is active
is_payroll_default boolean Whether this is the default contract for payroll
created_by_id integer ID of the creating user (read-only)
created_at datetime Creation timestamp (read-only)

Relationships

The following related objects can be included using the with parameter:

  • business_partner - The employee associated with the contract

Validation Rules

  • business_partner_id is required, and must name a business partner in the same client account
  • client_account_id is required
  • pension_provider_id, if provided, must name a business partner in the same client account
  • employment_type is required
  • start_date is required
  • salary_type must be MONTHLY_FIXED, HOURLY or COMMISSION
  • When salary_type is MONTHLY_FIXED, monthly_salary is required
  • When salary_type is HOURLY, hourly_rate is required
  • When salary_type is COMMISSION, monthly_salary and hourly_rate must be empty
  • vacation_pay_rate, pension_rate and employee_pension_rate are fractions below 1
  • work_percentage is between 0 and 100; weekly_hours is above 0 and at most 168

Error Responses

Status Code Description
400 Validation error (missing or invalid fields, or a business_partner_id/pension_provider_id that does not belong to this client account)
403 Forbidden (no access to client account)
404 Contract not found