Skip to content

Request an integration

Tell us which system you want to connect. We review every request weekly and publish a step-by-step guide or build the connection.

Periods and locking

Payroll period kinds, how export commit and period lock freeze hours, the preconditions for locking, and how to handle corrections after a period is closed.

Last updated: · Markdown version

Payroll period kinds

kind Range Notes
week ISO week, Monday–Sunday The default
four_weeks 28 days from an anchor Monday The admin sets the anchor in "Instellingen"; default anchor 2024-01-01
semi_month 1st–15th and 16th–end of month —
month Calendar month —

Admins set the org's period kind in "Instellingen" → "Organisatie en verloning". The API accepts any startsOn/endsOn range for exports and locks, so you can export a month in one call. The dashboard's overview, export and lock screens still work per week (known gap).

Two freezing steps

Step Endpoint Who Effect Event
Export commit POST /exports {"mode":"commit"} write key / office Approved days in range → exported. Approved expenses → exported. Approved leave days are recorded as exported (each leave day is exported once). An immutable export run is stored export.completed
Period lock POST /periods/lock {startsOn, endsOn, kind} admin key or admin user Exported days in range → locked. The period is recorded as locked period.locked

Both are irreversible. Typically you commit when the file goes to payroll and lock after payroll has processed it.

Commit rules

  • Only approved, conflict-free days are included. The preview's pendingCount tells you how many days in the range are still draft or submitted.
  • A commit fails with 422 when nothing is approved ("Nothing approved to export in this period") or when the mapping reports validation issues ("Export has N validation issue(s)").
  • Committing the same range twice within one minute returns 409 ("This period was just exported; refresh before exporting again").
  • Days exported in an earlier run are not exported again. A later commit over the same range only picks up days approved since.

Lock preconditions

POST /periods/lock refuses with 409 while:

  • any day in the range with minutes is still draft, submitted or approved (captured: {"code":"conflict","message":"1 day(s) in this period are not exported yet"}), or
  • any expense in the range is submitted or approved ("N expense(s) in this period are not exported or rejected yet").

Success returns { "data": { "days": 7 } }, the number of calendar days in the range.

Write keys get 403 {"code":"forbidden","message":"Admin role required"} on this endpoint. Use an API key with the admin scope, an admin user access token, or the dashboard ("Export" → "Periode vergrendelen"). tt lock and the MCP tool lock_period work when they run with an admin key.

Corrections after export or lock

Exported and locked days are immutable. A claim on such a day is refused with 423 {"code":"locked","message":"This day is exported or locked; book a correction in an open period"} and nothing is stored.

To correct:

  1. Record the difference as new hours on a date in an open period. For example, a manual_office claim on a day in the current week with note: "Correction for 2026-12-01: +30 min". A separate correction placement also works if your payroll needs it.
  2. Approve and export it in the next run. The payroll system processes the difference.
  3. For negative corrections, agree a procedure with your payroll provider. Pay lines are never negative.

Reading what was exported

  • GET /exports/runs lists committed runs: id, range, row count, content hash, created at. It is paginated.
  • GET /exports/runs/{id} returns the mapping snapshot, pay_lines and warnings.
  • GET /exports/runs/{id}/file returns the exact file as committed (text/csv).
  • GET /days?from=…&to=…&status=exported,locked shows which days are frozen, with export_run_id.