Skip to content

Payroll Export

Ditio generates payroll data automatically from the time registrations field workers submit and the payroll rules and absence codes configured in your company’s payroll setup. When a payroll period is approved, that data needs to reach your payroll or accounting system — this guide covers how to pull it out programmatically.

  • You run payroll in an external system (Visma, Tripletex, SAP, or a custom ERP) and want approved hours, overtime, allowances, and absences delivered automatically every period
  • You’re a payroll-system vendor building a standard Ditio connector
  • You want to replace a manual “download file, upload file” routine with a scheduled sync
  • API credentials with the ditioapiv3 scope — see Authentication. The user your API client acts as must be an administrator; other clients get 403 Forbidden on every payroll export endpoint
  • Payroll and absence codes configured in Ditio’s payroll module (without this, no payroll data is generated). Your payroll administrator sets this up — see the payroll articles in the Ditio help centre

Examples use the environment variables from Environments and default to the test environment. Examples get a token with scope=ditioapiv3 (in shell, into $TOKEN with DITIO_SCOPE=ditioapiv3, the default) as shown in Getting a token in your scripts.

The preferred method is the JSON payroll export API. The response contains all data needed to convert into any accounting system format.

GET /api/payroll-export/

GET /api/payroll-export/readonly is an alias for the same JSON export.

ParameterTypeRequiredDescription
fromWorkDatestringNoStart date, e.g. 2026-01-01. Defaults to 14 days before now when omitted. See Date ranges
toWorkDatestringNoEnd date, e.g. 2026-01-31. Defaults to now when omitted. See Date ranges
modifiedSinceDatestringNoReturn payroll data modified since this date. See Date ranges
dataFilterintNo0 = approved and not yet locked (default), 4 = not yet locked, approved or not, 5 = locked only, 10 = all data
userIdslist of stringNoFilter by specific Ditio user IDs. If omitted, returns data for all users
userPayrollTypeFilterstringNopaid-by-hour (default), fixed-pay, or all-users
overridePayrollExportSettingsForFixedPayEmployeesboolNoInclude regular hours and absences for fixed-pay employees
exportAllPayrollTypesboolNoInclude payroll types marked “do not export” in Ditio setup
includeAllAbsenceboolNoInclude absence transactions regardless of absence type
onlyDataFromExternalProjectsboolNoInclude only data registered in external projects
companyIdslist of stringNoFilter by project company IDs
projectIdslist of stringNoFilter by Ditio project IDs
payrollTypeIdslist of stringNoFilter by payroll type IDs
absenceTypeIdslist of stringNoFilter by absence type IDs

Dates are whole days; any time of day is ignored.

Without modifiedSinceDate, a work-date range can be at most 45 days. A longer range is not rejected: Ditio silently shortens it to 45 days from fromWorkDate and returns only that part, with 200 OK. To export a longer period, split it into chunks of 45 days or less (a calendar month always fits) and make one request per chunk.

With modifiedSinceDate, you get payroll data modified since that date, inside the work-date range you give:

  • Omit fromWorkDate and toWorkDate to search work dates from one year back to three months ahead.
  • That default range is longer than 45 days, so modifiedSinceDate must then be within the last 45 days. An older modifiedSinceDate together with a range over 45 days returns 400 Bad Request.
  • To pick up changes older than 45 days, pass a work-date range of 45 days or less with it.
Terminal window
curl "$DITIO_API_BASE/api/payroll-export/?fromWorkDate=2026-01-01&toWorkDate=2026-01-31&dataFilter=0" \
-H "Authorization: Bearer $TOKEN"
import os
import requests
# Paste get_token (/getting-started/authentication/#getting-a-token-in-your-scripts) above this.
API_BASE = os.environ.get("DITIO_API_BASE", "https://core-api.ditio.dev/core")
response = requests.get(
f"{API_BASE}/api/payroll-export/",
headers={"Authorization": f"Bearer {get_token('ditioapiv3')}"},
params={
"fromWorkDate": "2026-01-01",
"toWorkDate": "2026-01-31",
"dataFilter": 0, # only approved
},
)
response.raise_for_status()
payroll_data = response.json()
using System.Net.Http.Headers;
var apiBase = Environment.GetEnvironmentVariable("DITIO_API_BASE")
?? "https://core-api.ditio.dev/core";
using var http = new HttpClient();
// DitioAuth.GetTokenAsync: see /getting-started/authentication/#token-expiry
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", await DitioAuth.GetTokenAsync(http));
var url = $"{apiBase}/api/payroll-export/" +
"?fromWorkDate=2026-01-01&toWorkDate=2026-01-31&dataFilter=0";
var response = await http.GetAsync(url);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
GET /api/payroll-export/summary-as-lines

Returns payroll summaries as export lines. When fromWorkDate or toWorkDate is omitted, the endpoint uses the same default work-date range as the JSON export, and the same date-range rules apply.

GET /api/payroll-export/file

Returns a payroll export file in the format configured in your company’s payroll setup. Ditio supports flat-file formats compatible with several accounting systems — to request support for a new format, send a sample export file, field descriptions per column, and the accounting system’s import-format documentation to [email protected].

It takes fromWorkDate and toWorkDate like the JSON export, and a range over 45 days is cut to 45 days in the same way (see Date ranges). Additional parameters:

ParameterTypeRequiredDescription
voucherNumberstringNoVoucher number for cost file export
dataExportTypeintNo5 = preview, 6 = journal, Excel (default), 7 = extended journal, Excel, with every payroll line, 10 = payroll, 11 = secondary payroll file (only for companies set up for it; others get 400), 20 = absence, 30 = cost, 40 = transaction split
dataFilterintNo0 = approved and not yet locked (default), 4 = not yet locked, approved or not, 5 = locked only, 10 = all data
userPayrollTypeFilterstringNopaid-by-hour (default), fixed-pay, or all-users
payrollTypeIdslist of stringNoFilter by payroll type IDs
absenceTypeIdslist of stringNoFilter by absence type IDs

In the payroll file (dataExportType 10), a line is left out when its export ID is empty or has no letter or digit, such as . or -. The ID is the one that applies after any override or fixed-pay ID.

If you’re pulling payroll data into a BI tool or data warehouse (rather than feeding a payroll run), the paginated /v1/payroll-lines endpoints of the Data Extraction API may fit better. They live on $DITIO_REPORTING_BASE and need the reportingapiv1 scope — see Which scope for which host.

IssueCauseFix
Empty responseNo approved, unlocked data in the rangeCheck dataFilter — the default returns only approved data that isn’t locked yet. Use 10 to see everything, and verify the period is approved in Ditio
Data missing at the end of a long rangeRanges over 45 days are cut to 45 days from fromWorkDate, without an errorSplit the export into chunks of 45 days or less — see Date ranges
403 ForbiddenThe user your API client acts as isn’t an administratorHave an administrator give the client’s user administrator access in Ditio Web
Fixed-salary employees missingDefault filter is paid-by-hourSet userPayrollTypeFilter=all-users (and consider overridePayrollExportSettingsForFixedPayEmployees=true)
Some payroll types missingTypes marked “do not export” in setupSet exportAllPayrollTypes=true, or fix the type configuration
400 with “ModifiedSinceDate older than 45 days”modifiedSinceDate is more than 45 days back and the work-date range is longer than 45 daysAdd a work-date range of 45 days or less, or re-export the affected range without modifiedSinceDate
Lines of one type missing from the payroll fileThe export ID that applies to the line (including a fixed-pay or override ID) is empty or has no letter or digit, such as . or -Replace the empty or placeholder ID where it is set (the type’s export ID, its fixed-pay ID, or an override). Do not re-import a whole period: a new export can return lines you already imported, and the default dataFilter leaves out locked lines (a period locked in Ditio needs dataFilter=5). If you are unsure which lines are missing, contact [email protected] before importing

Ready-made Postman files with the token request and a pre-configured payroll export call are available — contact [email protected].