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.
When to use this
Section titled “When to use this”- 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
Prerequisites
Section titled “Prerequisites”- API credentials with the
ditioapiv3scope — see Authentication. The user your API client acts as must be an administrator; other clients get403 Forbiddenon 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.
Export methods
Section titled “Export methods”1. API export (recommended)
Section titled “1. API export (recommended)”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.
Query parameters
Section titled “Query parameters”| Parameter | Type | Required | Description |
|---|---|---|---|
fromWorkDate | string | No | Start date, e.g. 2026-01-01. Defaults to 14 days before now when omitted. See Date ranges |
toWorkDate | string | No | End date, e.g. 2026-01-31. Defaults to now when omitted. See Date ranges |
modifiedSinceDate | string | No | Return payroll data modified since this date. See Date ranges |
dataFilter | int | No | 0 = approved and not yet locked (default), 4 = not yet locked, approved or not, 5 = locked only, 10 = all data |
userIds | list of string | No | Filter by specific Ditio user IDs. If omitted, returns data for all users |
userPayrollTypeFilter | string | No | paid-by-hour (default), fixed-pay, or all-users |
overridePayrollExportSettingsForFixedPayEmployees | bool | No | Include regular hours and absences for fixed-pay employees |
exportAllPayrollTypes | bool | No | Include payroll types marked “do not export” in Ditio setup |
includeAllAbsence | bool | No | Include absence transactions regardless of absence type |
onlyDataFromExternalProjects | bool | No | Include only data registered in external projects |
companyIds | list of string | No | Filter by project company IDs |
projectIds | list of string | No | Filter by Ditio project IDs |
payrollTypeIds | list of string | No | Filter by payroll type IDs |
absenceTypeIds | list of string | No | Filter by absence type IDs |
Date ranges
Section titled “Date ranges”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
fromWorkDateandtoWorkDateto search work dates from one year back to three months ahead. - That default range is longer than 45 days, so
modifiedSinceDatemust then be within the last 45 days. An oldermodifiedSinceDatetogether with a range over 45 days returns400 Bad Request. - To pick up changes older than 45 days, pass a work-date range of 45 days or less with it.
Example
Section titled “Example”curl "$DITIO_API_BASE/api/payroll-export/?fromWorkDate=2026-01-01&toWorkDate=2026-01-31&dataFilter=0" \ -H "Authorization: Bearer $TOKEN"Python example — monthly sync
Section titled “Python example — monthly sync”import osimport 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()C# example
Section titled “C# example”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-expiryhttp.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();2. Summary lines
Section titled “2. Summary lines”GET /api/payroll-export/summary-as-linesReturns 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.
3. File export
Section titled “3. File export”GET /api/payroll-export/fileReturns 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:
| Parameter | Type | Required | Description |
|---|---|---|---|
voucherNumber | string | No | Voucher number for cost file export |
dataExportType | int | No | 5 = 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 |
dataFilter | int | No | 0 = approved and not yet locked (default), 4 = not yet locked, approved or not, 5 = locked only, 10 = all data |
userPayrollTypeFilter | string | No | paid-by-hour (default), fixed-pay, or all-users |
payrollTypeIds | list of string | No | Filter by payroll type IDs |
absenceTypeIds | list of string | No | Filter 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.
Payroll lines via the Data Extraction API
Section titled “Payroll lines via the Data Extraction API”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.
Common issues
Section titled “Common issues”| Issue | Cause | Fix |
|---|---|---|
| Empty response | No approved, unlocked data in the range | Check 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 range | Ranges over 45 days are cut to 45 days from fromWorkDate, without an error | Split the export into chunks of 45 days or less — see Date ranges |
403 Forbidden | The user your API client acts as isn’t an administrator | Have an administrator give the client’s user administrator access in Ditio Web |
| Fixed-salary employees missing | Default filter is paid-by-hour | Set userPayrollTypeFilter=all-users (and consider overridePayrollExportSettingsForFixedPayEmployees=true) |
| Some payroll types missing | Types marked “do not export” in setup | Set 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 days | Add 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 file | The 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 |
Postman collections
Section titled “Postman collections”Ready-made Postman files with the token request and a pre-configured payroll export call are available — contact [email protected].
Related
Section titled “Related”- Payroll Lines — paginated payroll extraction for BI
- Export Time Data — raw time registrations (pre-payroll)
- Authentication — credentials and scopes
- Help centre: Lønn — payroll setup for administrators (Norwegian)