/directory/api/4.0/work-contracts) is deprecated in favour of the Lucca API (v5), which replaces the
work-contract (representing the base contract) and work-contract-amendment (representing incremental historized changes to the base contract)
resources with different-typed and historized resources: employment and job-position.
No sunset date has been announced for
/directory/api/4.0/work-contracts yet. Make sure your API key has a valid contact email address so you
receive deprecation notices when a date is set.What Changes
In v4, a work-contract was a single record holding both the legal contract terms (start and end dates, contract type) and the employment context (establishment, occupation category). The Lucca API splits this into dedicated resources:employment
The legal contract: start/end dates, the legal entity, the contract template, and an optional external reference. One employment
covers a continuous period with the company.
job-position
The professional role during an employment: business establishment, department, manager, job title, occupation category, and more.
One employment may span multiple sequential job-positions when the employee’s role changes without the contract ending.
probationary-period
The trial period associated with an employment, if any. It has its own start and end dates, which may differ from the employment’s dates.
employee-attributes (unified read endpoint)
A uniform endpoint that surfaces any property from the employee, employment, and job-position resources — including custom extension
fields — in a single call. The recommended approach for integrations that want a comprehensive snapshot of an employee’s data without
chaining multiple requests.
establishmentId and spcId sat directly on the work-contract. In v5, these belong to the job-position, not the employment. This means a new job-position is created whenever only the role (not the contract itself) changes — it is no longer necessary to terminate and re-create a contract for role-only changes.
Snapshots replace diffs: the v4 work-contract amendment model stored incremental changes on top of a base contract (a “diff” approach). In contrast, employments and job-positions in v5 are “snapshot” resources that each record the full state for their period — a job-position stores the complete establishment, occupation category, manager, etc., not just what changed.
First: Switch to OAuth 2.0 and Set the Api-Version Header
First of all, replace your API key header with an OAuth 2.0 bearer token obtained from the Lucca authorization server. You will need to request the appropriate scopes for the resources you intend to read:| Resource | Required scope |
|---|---|
employment | employments.readonly |
job-position | job-positions.readonly |
probationary-periods | probationary-periods.readonly |
employee-attributes (values) | employee-attributes.readonly |
employee-attributes (definitions) | employee-attribute-definitions.readonly |
The
employee-attribute-definitions.readonly scope is only needed if you want to discover attribute IDs programmatically (e.g. to enumerate
all custom fields via GET /lucca-api/employee-attribute-definitions). If your integration already knows the definition IDs it needs,
employee-attributes.readonly is sufficient.Api-Version: {VERSION} HTTP header in all your requests. Currently, the
latest version is 2024-11-01.
Authentication
Read more about how to obtain and use OAuth 2.0 bearer tokens.
Versioning
Read more about how to use the
Api-Version header to specify the API version.Notable Breaking Changes
establishmentId moved to job-position
In v4, the establishment (establishmentId) was a property of the work-contract itself. In v5, it is a property
of the job-position (businessEstablishment). As a result, the same employment contract can now cover multiple
establishments across successive job-positions — you no longer need to terminate and re-create a contract simply
because the role changes.
legalEntity vs. businessEstablishment
The v4 establishmentId maps to the v5 jobPosition.businessEstablishment. Meanwhile, employment.legalEntity
is a new concept in v5 with no direct v4 equivalent — it is the legal entity on the employment contract, inferred
from the first job-position’s business establishment and displayed as “legal-unit” in the Lucca UI.
| V4 field | V5 resource | V5 field | V5 concept |
|---|---|---|---|
establishmentId | job-position | businessEstablishment | Where the employee physically works. |
| (no v4 equivalent) | employment | legalEntity | Legal entity (sometimes called “legal unit”) on the contract. |
Trial period day counts are gone
The v4trialPeriodDays and renewedTrialPeriodDays integer fields have no v5 equivalent. Only the computed end dates
(initialEndsOn and extendedEndsOn on the probationary-period resource) are stored.
Contract dates are now objects
The v4startsOn and endsOn were plain date strings. In v5, employment.start and employment.end are objects with a nested
date sub-field:
date sub-field.
IDs are strings in v5
All v5 resource identifiers are JSON strings ("id": "514") rather than integers ("id": 514). Do not use strict
equality (===) between v4 integer IDs and v5 string IDs.
Mapping v4 work-contract IDs to v5 employment IDs
Alternatively, if your v4 integration usedexternalId, that value is preserved as employment.remoteId in v5 — you can filter
employments by remoteId to find the corresponding v5 resource.
isApplicable fallback is not replicated in v5 (breaking change)
In v4, the isApplicable: true flag used a fallback rule: if no contract was current, it marked the next upcoming contract as applicable,
or the last contract if there was none. The v5 status: "active" does not replicate this fallback — an employee with a gap between
employments will have no active employment during that gap. If your integration relied on this fallback, adjust your query logic accordingly.
Recommended alternative: use the employee-attributes endpoint with the
applicability.asOf query parameter. The applicability field on each returned attribute provides start and end dates that define
when the value applies. This gives you explicit temporal boundaries instead of the implicit v4 fallback logic. For example:
Field Mapping Reference
Work-Contract = Employment + Job-Position + Employee-Attribute[]
| V4 work-contract field | V5 resource | V5 field | Notes |
|---|---|---|---|
id | employment | id | New ID; v4 and v5 IDs are not the same value — do not assume identity |
ownerId | employment / job-position | employee.id | Both resources reference the employee |
externalId | employment | remoteId | Renamed; no uniqueness constraint in v5 |
typeId | employment | template.id | Now a reference to the employment-template resource |
startsOn | employment | start.date | Now a nested object: { "date": "YYYY-MM-DD" } |
endsOn | employment | end.date | Same wrapping object; null for open-ended contracts |
isApplicable | employment | status | isApplicable: true ≈ status: "active". Read-only in both versions |
establishmentId | job-position | businessEstablishment.id | Moved from the contract to the job-position |
spcId | job-position | occupationCategory.id | Renamed; still references the same occupation categories |
trialPeriodEndDate | probationary-period | initialEndsOn | Moved to the separate probationary-period resource |
trialPeriodEndDate2 | probationary-period | extendedEndsOn | Same separate resource |
trialPeriodDays | (no equivalent) | — | Only the end date is stored in v5; the day count is not |
renewedTrialPeriodDays | (no equivalent) | — | Same — only the end date is stored |
hiringTypeId | employee-attribute | e_generated_hiringType | The hiring type is now stored as an employee attribute. |
temporaryContractGroundId | employee-attribute | e_generated_temporaryContractGround | The temporary contract ground is now stored as an employee attribute. |
internshipSupervisorId | employee-attribute | e_generated_internshipSupervisorId | The internship supervisor is now stored as an employee attribute. |
terminationReasonId | employee-attribute | e_generated_terminationReasonId | The termination reason is now stored as an employee attribute. |
createdAt | employment or job-position or employee-attribute | createdAt | Same semantics |
lastModifiedAt | employment or job-position or employee-attribute | lastUpdatedAt | Renamed |
authorId / lastModifierId | (no equivalent) | — | Authorship metadata not exposed in v5 while there’s no official “account” API resource. |
Fields migrated to
employee-attributes with the e_generated_ prefix are auto-generated attribute definitions created by Lucca when
migrating legacy contract data. Confirm their exact IDs by querying GET /lucca-api/employee-attribute-definitions.Querying in V5
All collection endpoints enforce cursor-based pagination. Start with
?limit={page_size}&include=links, then follow the links.next URL
in each response to get the next page. Iterate until links.next is null. The maximum page size is 100 items (default 25).Pagination
Learn how to paginate through collection endpoints in the Lucca API.
List all employments for an employee
Add
include=links to the request — this is required to receive pagination links in the response. Iterate until links.next is null to retrieve all pages.Retrieve a single employment by ID
List all job-positions for an employment
Get the probationary period for an employment
Further Reading
Migrating from /api/v3/users
Migration guide for the v3 user object, covering personal data, employment, and job-position fields.
Authentication
How to obtain and use OAuth 2.0 bearer tokens.
Employment resource
Full reference for the v5 employment resource.
Job-position resource
Full reference for the v5 job-position resource.
Get Started With Employee Attributes
How to use the unified employee-attributes endpoint to read any employee property in a single call.