Employees (v5)
The v5 Employees API is a modernized replacement for the v4 Users API. It provides cleaner semantics, true PATCH support, dedicated operation endpoints, and richer data includes (tags, employment history, work time arrangements).
Use v5 for new integrations. The v4 Users API remains available but will not receive new features.
Base URL: api/v5/integration/employees · Scope: ditioapiv3
# Test (default for all examples)export DITIO_API_BASE="https://core-api.ditio.dev/core"# Production: export DITIO_API_BASE="https://core-api.ditio.app/core"Key differences from v4
Section titled “Key differences from v4”| Feature | v4 Users API | v5 Employees API |
|---|---|---|
| Identifier | identityId / companyProfileId | employeeNumber (consistent across all endpoints) |
| PATCH | Partial update on companyProfileId | True PATCH semantics — omitted fields unchanged, explicit null clears values |
| Employment | Mixed into user create/update | Separate dedicated endpoints (create-employment, update-employment, end-employment) |
| Tags | Simple key-value pairs on create | Full tag management with date ranges, payroll tags, and diff-based updates |
| Includes | All data returned by default | Opt-in via ?include=tags,employment-history,work-time-arrangements |
| Search | By profile ID or employee number | Free-text search across name, phone, email, employee number |
| Multi-company | Manual per-company calls | Built-in includeEmployeesFromSubsidiaries parameter |
Create an employee
Section titled “Create an employee”POST /api/v5/integration/employeesCreates a new employee profile with an initial employment. The profile and first employment are created together — you cannot create a profile without an employment.
curl -X POST "$DITIO_API_BASE/api/v5/integration/employees" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "employeeNumber": "1042", "firstName": "Kari", "lastName": "Nordmann", "phone": "+4798765432", "birthDate": "1990-05-15", "email": "kari.nordmann@example.com", "workTitle": "Maskinfører", "employment": { "startDate": "2026-03-01", "department": "Anlegg", "payroll": 1 } }'Response: 201 Created with the full employee object, including the
created employment and its company context.
Required fields
Section titled “Required fields”| Field | Type | Description |
|---|---|---|
employeeNumber | string | Unique within the company structure |
firstName | string | First name |
lastName | string | Last name |
phone | string | Phone number (used as login username) |
birthDate | datetime | Date of birth |
employment | object | Initial employment (see below) |
employment.startDate | datetime | Employment start date |
Optional profile fields
Section titled “Optional profile fields”| Field | Type | Description |
|---|---|---|
email | string | Email address |
address | string | Home address |
workTitle | string | Job title |
carRegistrationNumber | string | Company car registration |
closestRelative | string | Emergency contact |
personalInfo | string | Additional personal info |
workCardId | string | Builder card / access card ID |
workCardExpirationDate | datetime | Card expiration date |
Employment fields
Section titled “Employment fields”| Field | Type | Default | Description |
|---|---|---|---|
startDate | datetime | required | Employment start date |
endDate | datetime | null | Employment end date (null = open-ended) |
department | string | null | Department name |
mainProjectNumber | string | null | Default project number |
payroll | int | 0 | Payroll type: 0 = Disabled, 1 = Enabled, 2 = Variable |
Get an employee
Section titled “Get an employee”GET /api/v5/integration/employees/{employeeNumber}| Parameter | Type | Default | Description |
|---|---|---|---|
includeEmployeesFromSubsidiaries | bool | false | Search across subsidiary companies |
include | string | Comma-separated: tags, work-time-arrangements, employment-history |
curl "$DITIO_API_BASE/api/v5/integration/employees/1042?include=tags,employment-history" \ -H "Authorization: Bearer $TOKEN"When employment-history is included, the response adds:
{ "employmentHistory": { "current": {}, "upcoming": [], "historical": [] }}List all employees
Section titled “List all employees”GET /api/v5/integration/employees| Parameter | Type | Default | Description |
|---|---|---|---|
includeEmployeesFromSubsidiaries | bool | false | Include employees from subsidiary companies |
include | string | Comma-separated: tags, work-time-arrangements, employment-history | |
employeeNumber | string | Filter by employee number |
Search employees
Section titled “Search employees”GET /api/v5/integration/employees/search?query={query}Free-text search across employee name, phone, email, and employee number.
Returns a lightweight response with hasActiveEmployment per hit.
curl "$DITIO_API_BASE/api/v5/integration/employees/search?query=Kari" \ -H "Authorization: Bearer $TOKEN"Update an employee
Section titled “Update an employee”Full update
Section titled “Full update”PUT /api/v5/integration/employees/{employeeNumber}Replaces all profile fields (firstName, lastName, birthDate required).
Employment changes should use the dedicated employment endpoints below.
Partial update
Section titled “Partial update”PATCH /api/v5/integration/employees/{employeeNumber}True PATCH semantics: only provided fields are updated, omitted fields remain
unchanged, and an explicit null clears a value.
curl -X PATCH "$DITIO_API_BASE/api/v5/integration/employees/1042" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "workTitle": "Prosjektleder", "email": null }'Patchable fields: firstName, lastName, email, phone, birthDate,
address, workTitle, carRegistrationNumber, closestRelative,
personalInfo.
Delete an employee
Section titled “Delete an employee”DELETE /api/v5/integration/employees/{employeeNumber}Returns 204 No Content.
GET /api/v5/integration/employees/{employeeNumber}/deletable{ "isDeletable": false, "validationErrors": [ "Employee has registered transactions", "Employee has active check-ins" ]}Disable / enable an employee
Section titled “Disable / enable an employee”Disabling prevents the employee from logging in. In a multi-company setup, this cascades to all companies and subcontractor relationships.
curl -X PATCH "$DITIO_API_BASE/api/v5/integration/employees/1042/disable" \ -H "Authorization: Bearer $TOKEN"
curl -X PATCH "$DITIO_API_BASE/api/v5/integration/employees/1042/enable" \ -H "Authorization: Bearer $TOKEN"Both return the updated employee response.
Employment operations
Section titled “Employment operations”End employment
Section titled “End employment”PATCH /api/v5/integration/employees/{employeeNumber}/end-employmentEnds the current active employment. In a multi-company setup, also ends employments in all associated project companies and subcontractor relationships.
curl -X PATCH "$DITIO_API_BASE/api/v5/integration/employees/1042/end-employment" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "endDate": "2026-06-30" }'Create new employment (rehire)
Section titled “Create new employment (rehire)”POST /api/v5/integration/employees/{employeeNumber}/create-employmentCreates a new employment for an existing employee. If a current active employment exists, it is ended the day before the new employment starts.
curl -X POST "$DITIO_API_BASE/api/v5/integration/employees/1042/create-employment" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "startDate": "2026-09-01", "department": "Maskinavdeling", "mainProjectNumber": "P-2001", "payroll": 1 }'Update current employment
Section titled “Update current employment”PATCH /api/v5/integration/employees/{employeeNumber}/update-employmentPartial update of the current active employment — only provided fields are updated.
Change employee number
Section titled “Change employee number”PATCH /api/v5/integration/employees/{employeeNumber}/change-employee-numbercurl -X PATCH "$DITIO_API_BASE/api/v5/integration/employees/1042/change-employee-number" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "newEmployeeNumber": "2042" }'The new employee number must be unique within the company structure.
Change phone number
Section titled “Change phone number”PATCH /api/v5/integration/employees/{employeeNumber}/change-phone-numberThis is an identity-level change that affects all company profiles — the phone number is the login username.
curl -X PATCH "$DITIO_API_BASE/api/v5/integration/employees/1042/change-phone-number" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "newPhoneNumber": "+4791234567" }'Project company operations
Section titled “Project company operations”Enable employee in project company
Section titled “Enable employee in project company”POST /api/v5/integration/employees/{employeeNumber}/enable-employee-in-project-companyActivates an employee in a specific project company with an optional user role.
| Field | Type | Required | Description |
|---|---|---|---|
projectCompanyId | string | Yes | The project company ID |
userGroupId | string | No | User group/role ID (defaults to user) |
Disable employee in project company
Section titled “Disable employee in project company”POST /api/v5/integration/employees/{employeeNumber}/disable-employee-in-project-companyBody: { "projectCompanyId": "COMPANY_ID" }.
Tag management
Section titled “Tag management”Replace all tags
Section titled “Replace all tags”PUT /api/v5/integration/employees/{employeeNumber}/tagsFull replacement of the employee’s tag list. The API computes the diff:
- Tags in the request but not existing → added
- Tags existing but not in the request → removed
- Tags with
userTagIdprovided → updated (only dates can change)
Payroll tags (isPayrollTag = true) allow multiple assignments with
non-overlapping date ranges. Regular tags allow only one assignment per tag.
curl -X PUT "$DITIO_API_BASE/api/v5/integration/employees/1042/tags" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '[ { "tagName": "Safety Course", "applicableFromDate": "2026-01-01", "applicableToDate": null } ]'| Field | Type | Description |
|---|---|---|
userTagId | string | For updates: the existing assignment ID. For new tags: omit |
tagId | string | The tag definition ID. For updates: required. For create: optional (use tagName instead) |
tagName | string | Tag name. For create: use this OR tagId. For updates: ignored |
applicableFromDate | datetime | Start date (required for payroll tags) |
applicableToDate | datetime | End date (null = open-ended) |
The response lists added, updated, removed, and currentTags.
Error responses
Section titled “Error responses”All errors follow the standard format:
{ "message": "Validation failed", "errors": [ { "category": "UserInvalidInputError", "code": "ValidationError", "field": "Phone", "message": "The Phone field is required." } ]}| Code | Meaning |
|---|---|
| 200 | Success |
| 201 | Created (POST endpoints) |
| 204 | Success, no body (DELETE) |
| 400 | Validation error — check the errors array |
| 404 | Employee not found |
Python example — HR sync skeleton
Section titled “Python example — HR sync skeleton”import osimport requests
API_BASE = os.environ.get("DITIO_API_BASE", "https://core-api.ditio.dev/core")headers = {"Authorization": f"Bearer {token}"}
def upsert_employee(hr_person): """Create the employee if missing, otherwise patch changed profile fields.""" number = hr_person["employee_number"] lookup = requests.get( f"{API_BASE}/api/v5/integration/employees/{number}", headers=headers ) if lookup.status_code == 404: payload = { "employeeNumber": number, "firstName": hr_person["first_name"], "lastName": hr_person["last_name"], "phone": hr_person["phone"], "birthDate": hr_person["birth_date"], "employment": {"startDate": hr_person["start_date"], "payroll": 1}, } requests.post( f"{API_BASE}/api/v5/integration/employees", headers=headers, json=payload, ).raise_for_status() else: changes = {"workTitle": hr_person["title"], "email": hr_person["email"]} requests.patch( f"{API_BASE}/api/v5/integration/employees/{number}", headers=headers, json=changes, ).raise_for_status()Related
Section titled “Related”- Users (v4) — the legacy API this replaces
- SCIM Provisioning — directory-driven provisioning
- Certificates — qualifications by employee number
- Interactive API reference — try the v5 endpoints in the browser