Export Time Data
This guide shows you how to pull time registration data from Ditio into your own systems. Common use cases include feeding a BI dashboard, syncing with a payroll provider, or archiving records in a data warehouse.
When to use this
Section titled “When to use this”- You want to export hours logged by field workers for payroll processing
- You’re building a BI dashboard that shows labor hours across projects
- You need to sync Ditio time data with an ERP system (SAP, Visma, etc.)
- You want to archive approved time registrations in your own database
Prerequisites
Section titled “Prerequisites”- API credentials with the
reportingapiv1scope — see Authentication - Understanding of Pagination (responses are paginated)
# Test (default for all examples)export DITIO_IDENTITY_BASE="https://identity.ditio.dev"export DITIO_REPORTING_BASE="https://core-api.ditio.dev/reporting"How it works
Section titled “How it works”Ditio stores time registrations when field workers log their hours via the mobile app. These records go through an approval workflow (field entry → foreman approval → payroll). The Data Extraction API lets you pull these records with a paginated, incremental-sync-friendly contract.
Step-by-step
Section titled “Step-by-step”1. Authenticate
Section titled “1. Authenticate”TOKEN=$(curl -s -X POST "$DITIO_IDENTITY_BASE/connect/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=client_credentials" \ -d "client_id=$CLIENT_ID" \ -d "client_secret=$CLIENT_SECRET" \ -d "scope=reportingapiv1" | jq -r '.access_token')2. Fetch time registrations
Section titled “2. Fetch time registrations”Query for a date range:
curl -H "Authorization: Bearer $TOKEN" \ "$DITIO_REPORTING_BASE/v1/time-registrations?FromDateTime=2026-01-01&ToDateTime=2026-01-31"3. Page through all results
Section titled “3. Page through all results”Follow continuationToken until all records are retrieved. See
Pagination.
4. Process the data
Section titled “4. Process the data”Each record includes the worker, project, work order, date and hours, and approval state — plus the IDs you can resolve against the metadata endpoints (users, projects, resources).
Complete example (Python)
Section titled “Complete example (Python)”import osimport requests
IDENTITY_BASE = os.environ.get("DITIO_IDENTITY_BASE", "https://identity.ditio.dev")REPORTING_BASE = os.environ.get("DITIO_REPORTING_BASE", "https://core-api.ditio.dev/reporting")
# Authenticateauth = requests.post(f"{IDENTITY_BASE}/connect/token", data={ "grant_type": "client_credentials", "client_id": CLIENT_ID, "client_secret": CLIENT_SECRET, "scope": "reportingapiv1",})token = auth.json()["access_token"]headers = {"Authorization": f"Bearer {token}"}
# Fetch all time registrations for January 2026records = []params = {"FromDateTime": "2026-01-01", "ToDateTime": "2026-01-31"}while True: page = requests.get( f"{REPORTING_BASE}/v1/time-registrations", headers=headers, params=params, ).json() records.extend(page["data"]) continuation = page.get("continuationToken") if not continuation: break params["ContinuationToken"] = continuation
print(f"Exported {len(records)} time registrations")C# example
Section titled “C# example”using System.Net.Http.Headers;using System.Net.Http.Json;using System.Text.Json;
var reportingBase = Environment.GetEnvironmentVariable("DITIO_REPORTING_BASE") ?? "https://core-api.ditio.dev/reporting";
using var http = new HttpClient();http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var allRecords = new List<JsonElement>();var baseQuery = $"{reportingBase}/v1/time-registrations" + "?FromDateTime=2026-01-01&ToDateTime=2026-01-31";var url = baseQuery;
while (url is not null){ var page = await http.GetFromJsonAsync<JsonElement>(url); allRecords.AddRange(page.GetProperty("data").EnumerateArray());
url = page.TryGetProperty("continuationToken", out var token) && token.ValueKind == JsonValueKind.String ? $"{baseQuery}&ContinuationToken={Uri.EscapeDataString(token.GetString()!)}" : null;}Incremental syncs
Section titled “Incremental syncs”For recurring exports, pass ModifiedSince = <last successful sync timestamp> instead of a work-date window. Incremental mode returns records
created and edited since that instant, so late corrections are picked up
without re-processing the entire history. See
Pagination.
Common issues
Section titled “Common issues”| Issue | Cause | Fix |
|---|---|---|
403 Forbidden | Token missing the reportingapiv1 scope | Request the scope explicitly when fetching the token |
| Edits missing from exports | Full sync only sees the date window | Use ModifiedSince incremental sync for recurring pulls |
| Slow unbounded pulls | No window on a large tenant | Chunk backfills month-by-month; keep ModifiedSince tight |
Related endpoints
Section titled “Related endpoints”- Payroll Export — processed payroll data (post-approval)
- Absence Registrations — leave and absence records
- User Metadata — user roster for matching worker IDs
- Project Metadata — project details for matching project IDs