Payroll Runs

Payroll runs represent individual payroll processing cycles. Each run covers a specific period and contains payroll lines for each employee. Payroll runs are a subtype of Documents with document_type set to PRRUN.

Endpoints

List Payroll Runs

GET /api/v2/payroll-runs

Retrieves a paginated list of payroll runs.

Query Parameters

Parameter Type Required Description
client_account_id integer Yes ID of the client account
from_date date No Filter runs with period_start on or after this date
to_date date No Filter runs with period_end on or before this date

Response

{
  "data": [
    {
      "id": 1,
      "created_by_id": 7,
      "created_at": "2024-03-15T10:00:00Z",
      "client_account_id": 7,
      "document_date": "2024-03-31",
      "document_number": "3",
      "period_start": "2024-03-01",
      "period_end": "2024-03-31",
      "uploaded_file_id": 456,
      "is_draft": false,
      "document_type": "PRRUN",
      "run_kind": "STANDARD"
    }
  ],
  "meta": {
    "page": 1,
    "pages": 1,
    "per_page": 100,
    "records": 1
  }
}

Create Payroll Run

POST /api/v2/payroll-runs

Creates a new payroll run. The run is created as a draft (is_draft: true).

When a STANDARD run is created, the system automatically seeds it with employees from active employment contracts that have is_payroll_default set to true. For each default contract, a base earning line (fixed salary or hourly pay; none for a COMMISSION contract) is created, and any configured default payroll lines and outstanding salary advances are also added. Seeding happens once: the seeded lines are ordinary lines that can be edited or deleted, and a recalculation never recreates them. After seeding, derived amounts (tax withholding, employer costs, etc.) are automatically recalculated.

A VACATION_PAYOUT run (feriepengekjøring) is instead seeded from accrued balances (fixed salary, holiday pay, and holiday-pay deduction) for the relevant earning year rather than from the default-line favourites. The earning year (opptjeningsaar) is not a request field — it is derived automatically as the year before period_end (falling back to document_date if period_end is not set), and either one is required on a VACATION_PAYOUT run.

Request Body

Field Type Required Description
client_account_id integer Yes The client account ID
document_date date Yes Payment date of the run. It decides which month’s a-melding reports the run
document_number string No Optional reference. A run is named by its period, not by a number
period_start date Yes Start date of the payroll period
period_end date Yes End date of the payroll period, on or after period_start
run_kind string No Kind of run: STANDARD (default), VACATION_PAYOUT or HALF_TAX

Example Request

{
  "client_account_id": 7,
  "document_date": "2024-03-31",
  "document_number": "3",
  "period_start": "2024-03-01",
  "period_end": "2024-03-31"
}

Response

Returns the created payroll run with status 201 Created. The run’s currency is the client account’s accounting currency.

Error Responses

Status Code Error Code Description
400 payroll.payroll_run_missing_dates period_start, period_end or document_date is missing, or period_start is after period_end
400 common.validation_error Unknown run_kind
403 payroll.client_account_access_denied No access to the client account, or client_account_id is missing

Update Payroll Run

POST /api/v2/payroll-runs/{id}
PUT /api/v2/payroll-runs/{id}

Updates an existing payroll run. Both POST and PUT methods are accepted. Only the fields below can be changed; run_kind and client_account_id are fixed when the run is created and are ignored here.

A posted run (is_draft: false) keeps its period and payment date: changing period_start, period_end or document_date on it answers 400 with payroll.payroll_run_not_draft, because the payment date decides which month’s a-melding reported the run. Reverse the run first to change them.

Request Body

Field Type Required Description
document_date date No Payment date of the run
document_number string No Optional reference
period_start date No Start date of the payroll period
period_end date No End date of the payroll period

Response

Returns the updated payroll run.

Delete Payroll Run

DELETE /api/v2/payroll-runs/{id}

Deletes a payroll run. Only draft runs can be deleted (is_draft must be true).

Response

Returns 204 No Content on success. Returns 400 Bad Request if the payroll run is not a draft.

Attributes

Attribute Type Description
id integer Unique identifier (read-only)
created_by_id integer ID of the creating user (read-only)
created_at datetime Creation timestamp (read-only)
client_account_id integer ID of the client account
document_date date Payment date of the run
document_number string Optional reference
period_start date Start date of the payroll period
period_end date End date of the payroll period
uploaded_file_id integer Associated voucher/file ID (read-only)
is_draft boolean Whether this is a draft run (read-only)
document_type string Always PRRUN (read-only)
run_kind string Kind of run: STANDARD (ordinary monthly run), VACATION_PAYOUT (feriepengekjøring) or HALF_TAX (December half-tax month: withholding follows the half-tax rules, while variable pay such as overtime and bonuses is withheld in full). A December run is only halved when it is created as HALF_TAX

Relationships

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

  • payroll_lines - Employee payroll lines for this run

Payroll lines support nested relations using dot notation (e.g. with=payroll_lines.item_type):

  • payroll_lines.item_type - The payroll item type definition
  • payroll_lines.business_partner - The employee
  • payroll_lines.contract - The employment contract
  • payroll_lines.tax_card - The employee’s tax card
  • payroll_lines.garnishment - The linked garnishment

Payroll Line Attributes

Attribute Type Description
id integer Unique identifier (read-only)
created_by_id integer ID of the creating user (read-only)
created_at datetime Creation timestamp (read-only)
document_id integer Parent payroll run ID (read-only)
business_partner_id integer Employee ID
item_type_id integer Payroll item type ID
contract_id integer Employment contract ID
tax_card_id integer Employee tax card ID
garnishment_id integer Linked garnishment ID (read-only)
description string Line description
quantity decimal Quantity (e.g. hours worked)
rate decimal Rate (e.g. hourly rate)
amount decimal Calculated amount
currency_code string Currency code
sort_order integer Display order
relation_type string Type of related object (read-only)
relation_id integer ID of related object (read-only)
is_company_cost boolean true for a cost of the company as a whole rather than of one employee, such as the pension premium from the provider’s invoice: an employer cost with no employment contract. Such lines appear on no payslip (read-only)

Error Responses

Status Code Error Code Description
400 payroll.payroll_run_missing_dates period_start, period_end or document_date is missing (create)
400 common.validation_error Unknown run_kind, or the client account doesn’t exist (create)
400 common.serialization_error Malformed request body
400 payroll.payroll_run_not_draft The run is posted: it can’t be deleted, and its period and payment date can’t change
403 payroll.client_account_access_denied No access to the client account (list, create)
403 payroll.payroll_run_access_denied No access to the payroll run’s client account
404 payroll.payroll_run_not_found Payroll run not found

Payroll Run Items

Payroll run items are the individual salary components (earnings, deductions, employer costs) within a payroll run. When items are added, updated, or deleted, the system automatically recalculates derived amounts (tax withholding, employer costs, etc.) for all employees in the run.

List Items

GET /api/v2/payroll/runs/{document_id}/items

Retrieves a paginated list of payroll line items for a specific run. The item_type relation is included by default.

Query Parameters

Parameter Type Required Description
page integer No Page number (default: 1)
per_page integer No Items per page (default: 1000)
with string No Include related resources (supported: item_type, business_partner, contract, tax_card, garnishment)

Response

{
  "data": [
    {
      "id": 501,
      "created_by_id": 7,
      "created_at": "2024-03-15T10:30:00Z",
      "document_id": 1,
      "business_partner_id": 56,
      "item_type_id": 10,
      "contract_id": 3,
      "tax_card_id": 12,
      "garnishment_id": null,
      "description": "Fastlønn",
      "quantity": "1.000000",
      "rate": "45000.000000",
      "amount": "45000.000000",
      "currency_code": "NOK",
      "sort_order": 1,
      "relation_type": null,
      "relation_id": null,
      "is_company_cost": false,
      "item_type": {
        "id": 10,
        "code": "FIXED_SALARY",
        "name_nob": "Fastlønn",
        "category": "EARNING"
      }
    }
  ],
  "meta": {
    "page": 1,
    "pages": 1,
    "per_page": 1000,
    "records": 1
  }
}

Add Item

POST /api/v2/payroll/runs/{document_id}/items

Adds a new payroll line item to a draft run. The item type must be active for the run’s period. Lines the payroll engine calculates (tax withholding, employer tax and the like) can’t be added by hand; the employer pension lines PENSION_EMPLOYER and AFP_PREMIUM are the exception. An earning or deduction needs an employee with an active employment contract in the client account. If this is the first item for a given employee, the employee joins the run: their base earning from the active contract (unless the new item is itself a base earning), default payroll lines and outstanding salary advances are seeded once. After adding, the system recalculates all derived amounts for the run.

Request Body

Field Type Required Description
business_partner_id integer Yes Employee ID
item_type_id integer Yes Payroll item type ID
contract_id integer No Ignored; set by the server on lines seeded from the employment contract
tax_card_id integer No Ignored; set by the server
description string No Line description
quantity decimal No Quantity (e.g. hours worked)
rate decimal No Rate (e.g. hourly rate)
amount decimal No Amount (auto-calculated if quantity and rate are set)
currency_code string No Currency code (default: account currency)
sort_order integer No Display order

Example Request

{
  "business_partner_id": 56,
  "item_type_id": 10,
  "contract_id": 3,
  "description": "Fastlønn",
  "quantity": "1",
  "rate": "45000",
  "amount": "45000"
}

Response

Returns the created item with status 201 Created.

Error Responses

Status Code Description
400 payroll.payroll_run_not_draft: the run is posted
400 payroll.payroll_line_auto_calculated: the item type is calculated by the payroll engine
400 payroll.employee_missing_contract: an earning or deduction for an employee with no active employment contract
400 Unknown item type or one inactive for the run’s period, or a business_partner_id that does not belong to the client account
403 payroll.payroll_run_access_denied: no access to the payroll run’s client account
404 payroll.payroll_run_not_found: payroll run not found

Update Item

POST /api/v2/payroll/runs/{document_id}/items/{item_id}
PUT /api/v2/payroll/runs/{document_id}/items/{item_id}

Updates an existing payroll line item. Both POST and PUT methods are accepted. After updating, the system recalculates all derived amounts for the run.

Lines the payroll engine calculates can’t be edited, except a manually added PENSION_EMPLOYER or AFP_PREMIUM line. A new item_type_id or business_partner_id is checked as on Add Item, and moving a line to an employee who is new to the run seeds that employee.

Request Body

Same fields as Add Item. Only provided fields are updated.

Response

Returns the updated item.

Error Responses

Status Code Description
400 payroll.payroll_run_not_draft: the run is posted
400 payroll.payroll_line_auto_calculated: the line is calculated by the payroll engine
400 payroll.employee_missing_contract: the line was moved to an employee with no active employment contract
400 The line is not on this payroll run, or a changed item type or employee is not valid
403 payroll.payroll_run_access_denied: no access to the payroll run’s client account
404 payroll.payroll_run_not_found: payroll run not found

Delete Item

DELETE /api/v2/payroll/runs/{document_id}/items/{item_id}

Removes a payroll line item from a draft run. Lines the payroll engine calculates can’t be deleted, except a manually added PENSION_EMPLOYER or AFP_PREMIUM line. After deletion, the system recalculates derived amounts. A deleted item stays deleted, including a base earning seeded from the contract, and the other items keep their ids. Deleting an employee’s last item takes them off the run; adding an item for them again seeds them anew.

Response

Returns 204 No Content on success.

Error Responses

Status Code Description
400 payroll.payroll_run_not_draft: the run is posted
400 payroll.payroll_line_auto_calculated: the line is calculated by the payroll engine
400 The line is not on this payroll run
403 payroll.payroll_run_access_denied: no access to the payroll run’s client account
404 payroll.payroll_run_not_found: payroll run not found

Posting and Reversal

Once a payroll run’s items are finalized, it can be posted to the general ledger. Posting creates journal entries with employee dimensions and generates a PDF voucher. If the earning period and payment date fall in different calendar months, periodization (accrual and reversal) entries are automatically generated.

Preview Posting

GET /api/v2/payroll/runs/{document_id}/posting-preview

Returns a preview of the journal entry that would be created when posting, without persisting anything. Useful for reviewing before committing. The response follows the same shape as GET /api/v2/journal-entries/{id} with lines and dimensions included, plus an account_name field on each line.

Example Response

{
  "id": null,
  "client_account_id": 7,
  "memo": "Lønnskjøring mars 2024",
  "lines": [
    {
      "line_id": 1,
      "posting_date": "2024-03-31",
      "account_code": "5000",
      "account_name": "Lønn til ansatte",
      "currency_code": "NOK",
      "debit": "45000.000000",
      "credit": "0.000000",
      "description": "",
      "dimensions": [
        {
          "relation_type": "BusinessPartner",
          "relation_id": 56
        }
      ]
    },
    {
      "line_id": 2,
      "posting_date": "2024-03-31",
      "account_code": "2610",
      "account_name": "Skattetrekk",
      "currency_code": "NOK",
      "debit": "0.000000",
      "credit": "15300.000000",
      "description": "",
      "dimensions": []
    }
  ]
}

Post Payroll Run

POST /api/v2/payroll/runs/{document_id}/post

Creates journal entries from the payroll document, generates a PDF voucher, and marks the run as finalized (is_draft becomes false). Also triggers automatic payslip email delivery and A-melding submission for the month of the payment date (document_date): the a-melding follows the cash basis, so a September run paid on 5 October is reported in October. The a-melding is queued right away once that month has started; for a later month, the monthly submission in the first days of the following month sends it. The run needs a payment date to be posted, and a posted run’s period and payment date can’t be changed.

Response

{
  "document_id": 1,
  "journal_entry_id": 42
}

Error Responses

Status Code Description
400 Document has already been posted, or has no items to post
403 payroll.payroll_run_access_denied: no access to the payroll run’s client account
404 payroll.payroll_run_not_found: payroll run not found

Reverse Payroll Run

POST /api/v2/payroll/runs/{document_id}/reverse

Reverses a posted payroll document’s journal entry and returns the run to draft status (is_draft becomes true), allowing items to be edited again.

Response

{
  "document_id": 1,
  "reversal_journal_entry_id": 43
}

Error Responses

Status Code Description
400 Cannot reverse a draft document, or no journal entry found
403 payroll.payroll_run_access_denied: no access to the payroll run’s client account
404 payroll.payroll_run_not_found: payroll run not found

Payslips

Generate and distribute payslips for employees in a payroll run.

List Payslips

GET /api/v2/payroll/runs/{document_id}/payslips

Returns payslip summaries for all employees in the payroll run.

Response

{
  "payslips": [
    {
      "employee_id": 56,
      "employee_name": "Ola Nordmann",
      "total_gross": "45000.000000",
      "total_deductions": "15300.000000",
      "net_pay": "29700.000000",
      "total_employer_costs": "6345.000000"
    }
  ]
}

Download Payslip PDF

GET /api/v2/payroll/runs/{document_id}/payslips/{bp_id}/pdf

Downloads a single employee’s payslip as a PDF file.

Path Parameters

Parameter Type Description
document_id integer Payroll run document ID
bp_id integer Employee business partner ID

Response

Returns a PDF file with Content-Type: application/pdf and Content-Disposition: attachment headers. The filename follows the pattern lonnsslipp_{period_from}_{period_to}_{bp_id}.pdf.

Error Responses

Status Code Description
403 payroll.payroll_run_access_denied: no access to the payroll run’s client account
404 payroll.payroll_run_not_found: payroll run not found; common.not_found: no payslip found for this employee

Send Payslips

POST /api/v2/payroll/runs/{document_id}/payslips/send

Queues payslip emails for all employees in the payroll run. Emails are sent asynchronously. Employees without an email address on file are skipped.

Response

{
  "message": "Payslip emails queued for delivery"
}

Returns status 202 Accepted.

Error Responses

Status Code Description
400 Cannot send payslips for a draft document
403 payroll.payroll_run_access_denied: no access to the payroll run’s client account
404 payroll.payroll_run_not_found: payroll run not found