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
Every endpoint requires an API client with the Administrator role. An
employee is looked up by employeeNumber within the company your token
belongs to; employees of project companies can’t be managed through this API
(403).
Examples use the environment variables from Environments and default to the test environment.
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 | Merge on companyProfileId; an explicit null is ignored | Merge on employeeNumber; an explicit null clears the value (except birthDate and phone) |
| Employment | Mixed into user create/update | Separate dedicated endpoints (create-employment, update-employment, end-employment) |
| Tags | Attach or detach by name; write-only, never returned | Full tag management with date ranges, payroll tags, and diff-based updates; readable via ?include=tags |
| Includes | Fixed response shape | Opt-in via ?include=tags,employment-history,work-time-arrangements |
| Lookup | By profile ID or employee number; no free-text search | By employee number, plus free-text search across name, phone, email and employee number |
| Subsidiaries | The list includes subsidiaries for a main-company token; filter with companyId | Your own company by default; opt in with includeEmployeesFromSubsidiaries |
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": "[email protected]", "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.
400 is returned when the employee number is already used in the company;
409 when the phone number belongs to another active employee in your
company structure.
Required fields
Section titled “Required fields”| Field | Type | Description |
|---|---|---|
employeeNumber | string | Must not already be used in the company |
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 and workCardExpirationDate are ignored on create. Set them
afterwards with PUT.
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 = NotApplicable, 1 = Variable (the same as payroll: true in the v4 Users API) |
Get an employee
Section titled “Get an employee”GET /api/v5/integration/employees/{employeeNumber}| Parameter | Type | Default | Description |
|---|---|---|---|
includeEmployeesFromSubsidiaries | bool | false | Also look in subsidiary companies. Only applies when your token belongs to the main company |
include | string | Comma-separated: tags, work-time-arrangements, employment-history |
Returns 404 when no employee has that number, and 400 when the number
matches employees in more than one company.
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. Only applies when your token belongs to the main company |
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}Case-insensitive substring search across employee name, phone, email, and
employee number. Returns at most 50 hits, as a lightweight response with
hasActiveEmployment per hit.
| Parameter | Type | Default | Description |
|---|---|---|---|
query | string | Text to search for | |
includeEmployeesFromSubsidiaries | bool | false | Also search subsidiary companies. Only applies when your token belongs to the main company |
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}firstName, lastName and birthDate are required. Despite the verb, PUT
is not a full replace:
firstName,lastNameandemailare always overwritten, so leaving outemailclears it.- Every other field keeps its stored value when you omit it or send
null. This is also the only way to setworkCardIdandworkCardExpirationDate. - A
phonethat differs from the stored one changes the login number, as Change phone number does.
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. The exceptions are
birthDate and phone, where null is ignored.
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, or 400 when the employee can’t be deleted.
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. Both endpoints change only the employee’s profile in your token’s company; profiles the same person has in other companies, such as project companies, are left as they are.
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-employmentSets endDate on the current active employment of the employee’s profile in
your token’s company. Employments in other companies are not changed. Returns
404 when there is no active employment.
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 and returns 201 Created.
If a current active employment exists, it is ended the day before the new
employment starts; the new startDate must then be later than the current
employment’s start date, otherwise the call returns 400.
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. Returns 404 when there is no active employment.
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 not already be used in the company (400).
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. Returns 409 when the number belongs to
another active user.
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”POST /{employeeNumber}/enable-employee-in-project-company and
POST /{employeeNumber}/disable-employee-in-project-company appear in the
interactive reference but are not implemented: both
return 501 Not Implemented. Don’t call them.
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.
Errors
Section titled “Errors”Errors use the standard Integration API error body and status codes; see Errors. The endpoint-specific cases are listed with each endpoint above.
Python example — HR sync skeleton
Section titled “Python example — HR sync skeleton”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")headers = {"Authorization": f"Bearer {get_token('ditioapiv3')}"}
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