Changelog
Die Spezifikation der REST-API wird aus dem Quellcode erzeugt und mit jedem ZEP-Release veröffentlicht. Seit der Umstellung auf die generierte Spezifikation beschreibt die Versionsnummer den API-Vertrag (1.x.y), nicht mehr das Produkt-Release: eine brechende Änderung erhöht die Hauptversion und landet in einem neuen /api/v2, additive Änderungen bleiben in v1. Releases nach der Umstellung schreibt die Release-Pipeline aus dem Diff der Spezifikation (Änderungstexte auf Englisch); sie erscheinen nur, wenn sich die API geändert hat. Das Release der Umstellung und alles davor ist von Hand geschrieben.
v8.0.90.2 · OpenAPI 1.2.0 · 05.11.2026
PUT/PATCH /employees/{employee}/regular-working-times/{id} keeps the holiday calendar
An update without holiday_calendar_id reset the schedule’s holiday calendar to the default calendar. It now stays as it is; only an explicit holiday_calendar_id changes it. POST without it still sets the default calendar.
v8.0.84 · OpenAPI 1.2.0 · 02.11.2026
limit is honoured: GET /projects/{id}/tasks
The task list ignored limit and always returned 15 tasks per page; perPage accepted only 5, 10 or 15. limit now sets the page size, 1 to 200, and a value outside that range answers 422, as on every other list. perPage is ignored on this endpoint, the same as elsewhere (the published specification still lists 5, 10, 15 for it), and limit is declared in the published specification.
v8.0.75 · OpenAPI 1.0.0 → 1.1.0 · 30.10.2026
Billing lock covers stored bookings: PUT, PATCH and DELETE /attendances/{id}
A booking inside an invoiced period (approved or billed invoice item) or attached to an invoice item can
no longer be changed or deleted: the request answers 422 Billing is blocked. and nothing is saved.
Before, only the new state of a PUT was checked, so a locked booking could be moved out, made
non-billable or deleted. The lock on these three requests can be switched off per tenant; switched off, they behave
as before this change.
Nested lists honour limit: GET /customers/{customer}/price-tables, /customers/{customer}/contacts, /departments/{department}/children, /departments/{department}/employees, /employees/{employee}/absences, /employees/{employee}/meals, /employees/{employee}/transponders, /offers/{offer}/items, /projects/{project}/activities, /projects/{project}/locations, /receipts/{receipt}/amounts, /tickets/{ticket}/subtasks, /devices
These lists ignored limit and always answered 15 entries per page. They now take limit (1 to 200, 422 outside it, default 15)
and page, in a stable order, and the page links keep limit. Besides limit and page they accept no parameters, except GET /departments, which keeps its start_date and end_date filters and its orderBy and order sorting and now honours limit as well.
Pagination links keep the query: GET /plannings
links.first, prev, next and last of GET /plannings dropped the query string, so following next returned unfiltered data, 15 per page, instead of the page of the request’s modified_after, limit, include_defaults and other filters. They now carry every parameter of the request, with and without include_defaults. The other paginated lists (for example GET /customers/{customer_number}/contacts) pass the query string through in their links as well, which changes nothing in what they return; the nested ones also take limit.
Billing lock for bookings: POST /attendances, PUT /attendances/{id}
A billable booking into a period that an approved or billed invoice item of the project covers is now rejected with 422 and the message “Billing is blocked.”, as in the web UI. Moving an existing booking into such a period is rejected the same way, and a booking already attached to an invoice item stays locked. Needs the invoicing module; the Jira worklog import skips such bookings with the same reason.
Alle Einzeländerungen (2)
- NEU
GET /customers/{customer_number}/contacts,GET /customers/{customer_number}/price-tables,GET /departments/{id}/children,GET /departments/{id}/employees,GET /devices,GET /employees/{username}/absences,GET /employees/{username}/meals,GET /employees/{username}/transponders,GET /offers/{id}/items,GET /projects/{id}/activities,GET /projects/{id}/locations,GET /receipts/{id}/amounts,GET /tickets/{id}/subtasks: added the new optionalqueryrequest parameterlimit - NEU
GET /customers/{customer_number}/contacts,GET /customers/{customer_number}/price-tables,GET /departments/{id}/children,GET /departments/{id}/employees,GET /devices,GET /employees/{username}/absences,GET /employees/{username}/meals,GET /employees/{username}/transponders,GET /offers/{id}/items,GET /projects/{id}/activities,GET /projects/{id}/locations,GET /receipts/{id}/amounts,GET /tickets/{id}/subtasks: added the non-success response with the status422
v8.0.59 · OpenAPI 1.0.0 · 28.10.2026
Rate limit: all customer operations
The specification now documents the limit the API has enforced: 1000 requests per 5 minutes per tenant, plus at most 25 concurrent requests per tenant. Every operation lists the 429 answer ({"message": "Too Many Attempts."} with Retry-After, X-RateLimit-Reset, X-RateLimit-Limit, X-RateLimit-Remaining), and every success response lists X-RateLimit-Limit and X-RateLimit-Remaining. Documentation only, no behaviour change.
Tasks: all_project_employees on GET /projects/{id}/tasks
Every task now reports all_project_employees: true when all project employees may book on it, false when only the
employees listed under GET /projects/{id}/tasks/{task_id}/employees may. The employee list of a task with
all_project_employees: true is empty on purpose.
Employees: cost_center, note, overtime_ads and project_number
GET/POST/PUT /employees and GET /departments/{id}/employees now return cost_center (Kostenstelle, separate from
cost_bearer), note and overtime_ads (boolean, null when never set). Project assignments on
/employees/{employee}/projects and /projects/{project}/employees carry project_number. Licence: as before.
Employees: include for employment periods, regular working times and internal rates
GET /employees and GET /employees/{employee} accept include=employment_periods,regular_working_times,internal_rates
(any subset, comma-separated) and embed those lists in each employee under the same key, in the shape of the
per-employee endpoints; the lists are complete, not paged, and empty ones are []. An unknown value answers 422; on a Clock licence internal_rates is unknown, as the internal-rates endpoint is not offered there either.
With include=internal_rates, modified_after also finds an employee whose internal rate changed, and modified_before leaves out one whose internal rate changed after the bound; a deleted internal
rate leaves no timestamp and is not detected. Employment period and working time changes already move the
employee’s modified. Without include nothing changes. Licence: as before.
Project assignments: GET /project-employees
Lists the project assignments of all projects in one paginated call, ordered by id. Filter by
project_id and employee_id (a single value or a list), project_status, project_start_date
and project_end_date. Not available on ZEP Clock.
Project assignments of a project: GET /projects/{project}/employees
project_status, project_start_date and project_end_date were accepted but ignored; they now
narrow the list like they do on GET /employees/{employee}/projects.
Invoice filters and positions: GET /invoices
New query parameters modified_after (exclusive) and modified_before (inclusive) restrict the list
to invoices changed in a time window; the modified value of a response works as the next
modified_after. start_date and end_date (YYYY-MM-DD, both inclusive, either one alone is
allowed) filter by the invoice date. include=items embeds each invoice’s positions as items, in the
form of GET /invoices/{invoice}/items, so no call per invoice is needed; without include the key is
absent. An unknown include value answers 422. A position change does not necessarily move the
invoice’s modified.
A client generated from the specification compiles: GET /plannings, GET /employees, GET /folders/{id}/documents, GET /folders/{id}/documents/{document_id}, GET /projects/{id}/employees, GET /employees/{id}/projects
The specification described planning_details.type and unit as always null and listed an empty value for salutation.name; both now match the answers (type is percentage, hours, time_range, days or null, unit is %, hours, time, days or null; salutation.name is a plain string). Document and ProjectEmployee are documented as the object they return, without the empty-array alternative. A document in a folder without a file system answers file_contents: null with filename and type, no longer an empty array, and the planning times from, to, from_time and to_time are documented as HH:MM:SS strings, not date-times. The values themselves do not change.
Task employees: no stored assignments while the task is open to all project employees
The list is now empty while the task has all_project_employees: true, even if assignments from an earlier explicit setup are still stored, and GET /projects/{id}/tasks/{task_id}/employees/{id} returns 404 for such an assignment. Booking already ignored them.
Employee sub-lists honour limit and page: GET /employees/{employee}/employment-periods, /regular-working-times,
/internal-rates
These three lists ignored limit and always answered 15 entries per page. They now page like every other list: limit
1 to 200 (422 outside it), default 15, plus page. A client that sent limit and relied on it being ignored gets the
requested page size.
Absence creation answers with the real outcome: POST /absences
A created absence now answers 201 instead of 200. A request ZEP rejects on business grounds (for
example an absence before the introduction date of the absence module) answers 422 with ZEP’s code,
message and the message again under errors.absence, and any other failure on the ZEP side answers 502; both used to come back as 200.
Only ZEP code 104 counts as a rejection (ZEP also answers a storage error with it); every other ZEP error code answers 502.
A half-day (type 1) or hourly (type 2) absence is now stored with that type; it used to be stored as a full day.
timezone is optional for every absence type and defaults to the server’s timezone.
Absences return created and modified: GET /absences, GET /absences/{id}
created and modified were always null on absences, on the embedded absenceReason and on
GET /absence-reasons and GET /articles; they now carry the stored timestamps, in UTC. Legacy rows
with a zero-date (0000-00-00 00:00:00) still answer null.
The specification matches the responses: types, nullable fields, date formats, cursor
Documentation only, no response changes. ProjectStatus.id is a string (the short name, e.g. Aktiv), Department.id is an integer, Project.keywords is a list of strings. CustomerContact.primary, Invoice.correction_date, Invoice.email_sent_at, Invoice.email_recipient, Invoice.note, Price.price and Project.project_status_id can be null. start_date of a price table, end_date of an attendance and from/to of a project task employee carry format: date. Invoice.correction_date is documented as local time Y-m-d H:i:s without a zone. On GET /attendances, modified_after is an inclusive cursor paired with id_after (default 0, effective only together with modified_after). oasdiff rates the type and nullability corrections as breaking; the release changelog marks them as fixes, because the frozen contract already says the same. The narrowed formats and Department.id, Project.keywords stay as oasdiff rates them.
Alle Einzeländerungen (144)
- KORREKTUR
GET /customers,GET /projects: the response propertydata/items/priceTables/items/prices/items/pricebecame nullable for the status200 - KORREKTUR
GET /customers,GET /projects: thedata/items/priceTables/items/start_dateresponse’s propertyformatchanged fromdate-timetodatefor status200 - KORREKTUR
POST /customers: the response propertydata/allOf[subschema #1: CustomerResource]/priceTables/items/prices/items/pricebecame nullable for the status201 - KORREKTUR
POST /customers: thedata/allOf[subschema #1: CustomerResource]/priceTables/items/start_dateresponse’s propertyformatchanged fromdate-timetodatefor status201 - KORREKTUR
GET /customers/{customer_number},GET /projects/{id}: the response propertydata/priceTables/items/prices/items/pricebecame nullable for the status200 - KORREKTUR
GET /customers/{customer_number},GET /projects/{id}: thedata/priceTables/items/start_dateresponse’s propertyformatchanged fromdate-timetodatefor status200 - KORREKTUR
PATCH /customers/{customer_number},PUT /customers/{customer_number}: the response propertydata/allOf[subschema #1: CustomerResource]/priceTables/items/prices/items/pricebecame nullable for the status200 - KORREKTUR
PATCH /customers/{customer_number},PUT /customers/{customer_number}: thedata/allOf[subschema #1: CustomerResource]/priceTables/items/start_dateresponse’s propertyformatchanged fromdate-timetodatefor status200 - KORREKTUR
GET /customers/{customer_number}/contacts: the response propertydata/items/primarybecame nullable for the status200 - KORREKTUR
POST /customers/{customer_number}/contacts: the response propertydata/allOf[subschema #1: CustomerContactResource]/primarybecame nullable for the status201 - KORREKTUR
GET /customers/{customer_number}/contacts/{id}: the response propertydata/primarybecame nullable for the status200 - KORREKTUR
PATCH /customers/{customer_number}/contacts/{id},PUT /customers/{customer_number}/contacts/{id}: the response propertydata/allOf[subschema #1: CustomerContactResource]/primarybecame nullable for the status200 - KORREKTUR
GET /customers/{customer_number}/price-tables,GET /projects/{id}/price-tables: the response propertydata/items/prices/items/pricebecame nullable for the status200 - KORREKTUR
GET /customers/{customer_number}/price-tables,GET /projects/{id}/price-tables: thedata/items/start_dateresponse’s propertyformatchanged fromdate-timetodatefor status200 - KORREKTUR
GET /customers/{customer_number}/price-tables/{id},GET /projects/{id}/price-tables/{price_table_id}: the response propertydata/prices/items/pricebecame nullable for the status200 - KORREKTUR
GET /customers/{customer_number}/price-tables/{id},GET /projects/{id}/price-tables/{price_table_id}: thedata/start_dateresponse’s propertyformatchanged fromdate-timetodatefor status200 - KORREKTUR
GET /invoices: the response propertydata/items/correction_datebecame nullable for the status200 - KORREKTUR
GET /invoices: the response propertydata/items/email_recipientbecame nullable for the status200 - KORREKTUR
GET /invoices: the response propertydata/items/email_sent_atbecame nullable for the status200 - KORREKTUR
GET /invoices: the response propertydata/items/notebecame nullable for the status200 - KORREKTUR
GET /invoices/{invoice_number}: the response propertydata/correction_datebecame nullable for the status200 - KORREKTUR
GET /invoices/{invoice_number}: the response propertydata/email_recipientbecame nullable for the status200 - KORREKTUR
GET /invoices/{invoice_number}: the response propertydata/email_sent_atbecame nullable for the status200 - KORREKTUR
GET /invoices/{invoice_number}: the response propertydata/notebecame nullable for the status200 - KORREKTUR
GET /plannings: thedata/items/fromresponse’s propertyformatchanged fromdate-timetononefor status200 - KORREKTUR
GET /plannings: thedata/items/toresponse’s propertyformatchanged fromdate-timetononefor status200 - KORREKTUR
GET /projects: the response propertydata/items/project_status_idbecame nullable for the status200 - KORREKTUR
GET /projects: thedata/items/status/allOf[subschema #1: ProjectStatusResource]/idresponse’s propertytypechanged fromintegertostringfor status200 - KORREKTUR
POST /projects: the response propertydata/allOf[subschema #1: ProjectResource]/priceTables/items/prices/items/pricebecame nullable for the status201 - KORREKTUR
POST /projects: the response propertydata/allOf[subschema #1: ProjectResource]/project_status_idbecame nullable for the status201 - KORREKTUR
POST /projects: thedata/allOf[subschema #1: ProjectResource]/priceTables/items/start_dateresponse’s propertyformatchanged fromdate-timetodatefor status201 - KORREKTUR
POST /projects: thedata/allOf[subschema #1: ProjectResource]/status/allOf[subschema #1: ProjectStatusResource]/idresponse’s propertytypechanged fromintegertostringfor status201 - KORREKTUR
GET /projects/{id}: the response propertydata/project_status_idbecame nullable for the status200 - KORREKTUR
GET /projects/{id}: thedata/status/allOf[subschema #1: ProjectStatusResource]/idresponse’s propertytypechanged fromintegertostringfor status200 - KORREKTUR
PATCH /projects/{id},PUT /projects/{id}: the response propertydata/allOf[subschema #1: ProjectResource]/priceTables/items/prices/items/pricebecame nullable for the status200 - KORREKTUR
PATCH /projects/{id},PUT /projects/{id}: the response propertydata/allOf[subschema #1: ProjectResource]/project_status_idbecame nullable for the status200 - KORREKTUR
PATCH /projects/{id},PUT /projects/{id}: thedata/allOf[subschema #1: ProjectResource]/priceTables/items/start_dateresponse’s propertyformatchanged fromdate-timetodatefor status200 - KORREKTUR
PATCH /projects/{id},PUT /projects/{id}: thedata/allOf[subschema #1: ProjectResource]/status/allOf[subschema #1: ProjectStatusResource]/idresponse’s propertytypechanged fromintegertostringfor status200 - INFO removed the schema
Json - INFO a breaking change was detected but the version is still
1.0.0 - NEU
GET /absence-reasons,GET /absence-reasons/{name},GET /absences,GET /absences/{id},GET /activities,GET /activities/{activity},GET /articles,GET /articles/{id},GET /attendances,DELETE /attendances/{id},GET /attendances/{id},PATCH /attendances/{id},PUT /attendances/{id},GET /categories,GET /categories/{name},GET /customers,DELETE /customers/{customer_number},GET /customers/{customer_number},PATCH /customers/{customer_number},PUT /customers/{customer_number},GET /customers/{customer_number}/contacts,DELETE /customers/{customer_number}/contacts/{id},GET /customers/{customer_number}/contacts/{id},PATCH /customers/{customer_number}/contacts/{id},PUT /customers/{customer_number}/contacts/{id},GET /customers/{customer_number}/price-tables,GET /customers/{customer_number}/price-tables/{id},GET /departments,GET /departments/{id},GET /departments/{id}/children,GET /departments/{id}/employees,GET /devices,GET /devices/{id},GET /dynamic-attributes,GET /dynamic-attributes/{id},GET /employees,GET /employees/{username},PATCH /employees/{username},PUT /employees/{username},GET /employees/{username}/absences,GET /employees/{username}/employment-periods,GET /employees/{username}/employment-periods/{id},PATCH /employees/{username}/employment-periods/{id},PUT /employees/{username}/employment-periods/{id},GET /employees/{username}/internal-rates,GET /employees/{username}/internal-rates/{id},GET /employees/{username}/meals,GET /employees/{username}/meals/{id},GET /employees/{username}/projects,GET /employees/{username}/regular-working-times,GET /employees/{username}/regular-working-times/{id},PATCH /employees/{username}/regular-working-times/{id},PUT /employees/{username}/regular-working-times/{id},GET /employees/{username}/transponders,GET /folders,GET /folders/{id},GET /folders/{id}/documents,GET /folders/{id}/documents/{document_id},GET /invoice-items,GET /invoice-items/{id},GET /invoices,GET /invoices/{invoice_number},GET /invoices/{invoice_number}/attachments,GET /invoices/{invoice_number}/items,GET /location-lists,GET /location-lists/{id},GET /location-lists/{id}/locations,GET /locations,GET /locations/{name},GET /offers,GET /offers/{id},GET /offers/{id}/items,GET /offers/{id}/items/{item_id},GET /planning,GET /plannings,GET /price-groups,GET /price-groups/{id},GET /projects,DELETE /projects/{id},GET /projects/{id},PATCH /projects/{id},PUT /projects/{id},GET /projects/{id}/activities,GET /projects/{id}/employees,GET /projects/{id}/invoice-items,GET /projects/{id}/locations,GET /projects/{id}/price-tables,GET /projects/{id}/price-tables/{price_table_id},GET /projects/{id}/tasks,DELETE /projects/{id}/tasks/{task_id},GET /projects/{id}/tasks/{task_id},PATCH /projects/{id}/tasks/{task_id},PUT /projects/{id}/tasks/{task_id},GET /projects/{id}/tasks/{task_id}/employees,GET /projects/{id}/tasks/{task_id}/employees/{project_task_employee_id},GET /receipts,POST /receipts,GET /receipts-amounts,DELETE /receipts/{id},GET /receipts/{id},PATCH /receipts/{id},PUT /receipts/{id},GET /receipts/{id}/amounts,GET /receipts/{id}/amounts/{amount_id},DELETE /receipts/{id}/attachments,GET /receipts/{id}/attachments,PUT /receipts/{id}/attachments,GET /resource-plannings,GET /tickets,POST /tickets,DELETE /tickets/{id},GET /tickets/{id},PATCH /tickets/{id},PUT /tickets/{id},POST /tickets/{id}/attachments,GET /tickets/{id}/subtasks,POST /tickets/{id}/subtasks,DELETE /tickets/{id}/subtasks/{subtask_id},GET /tickets/{id}/subtasks/{subtask_id},PATCH /tickets/{id}/subtasks/{subtask_id},PUT /tickets/{id}/subtasks/{subtask_id},GET /vehicles,GET /workhour-balances,GET /workhour-balances/{id}: the response headerx-ratelimit-limitwas added for the status200 - NEU
GET /absence-reasons,GET /absence-reasons/{name},GET /absences,GET /absences/{id},GET /activities,GET /activities/{activity},GET /articles,GET /articles/{id},GET /attendances,DELETE /attendances/{id},GET /attendances/{id},PATCH /attendances/{id},PUT /attendances/{id},GET /categories,GET /categories/{name},GET /customers,DELETE /customers/{customer_number},GET /customers/{customer_number},PATCH /customers/{customer_number},PUT /customers/{customer_number},GET /customers/{customer_number}/contacts,DELETE /customers/{customer_number}/contacts/{id},GET /customers/{customer_number}/contacts/{id},PATCH /customers/{customer_number}/contacts/{id},PUT /customers/{customer_number}/contacts/{id},GET /customers/{customer_number}/price-tables,GET /customers/{customer_number}/price-tables/{id},GET /departments,GET /departments/{id},GET /departments/{id}/children,GET /departments/{id}/employees,GET /devices,GET /devices/{id},GET /dynamic-attributes,GET /dynamic-attributes/{id},GET /employees,GET /employees/{username},PATCH /employees/{username},PUT /employees/{username},GET /employees/{username}/absences,GET /employees/{username}/employment-periods,GET /employees/{username}/employment-periods/{id},PATCH /employees/{username}/employment-periods/{id},PUT /employees/{username}/employment-periods/{id},GET /employees/{username}/internal-rates,GET /employees/{username}/internal-rates/{id},GET /employees/{username}/meals,GET /employees/{username}/meals/{id},GET /employees/{username}/projects,GET /employees/{username}/regular-working-times,GET /employees/{username}/regular-working-times/{id},PATCH /employees/{username}/regular-working-times/{id},PUT /employees/{username}/regular-working-times/{id},GET /employees/{username}/transponders,GET /folders,GET /folders/{id},GET /folders/{id}/documents,GET /folders/{id}/documents/{document_id},GET /invoice-items,GET /invoice-items/{id},GET /invoices,GET /invoices/{invoice_number},GET /invoices/{invoice_number}/attachments,GET /invoices/{invoice_number}/items,GET /location-lists,GET /location-lists/{id},GET /location-lists/{id}/locations,GET /locations,GET /locations/{name},GET /offers,GET /offers/{id},GET /offers/{id}/items,GET /offers/{id}/items/{item_id},GET /planning,GET /plannings,GET /price-groups,GET /price-groups/{id},GET /projects,DELETE /projects/{id},GET /projects/{id},PATCH /projects/{id},PUT /projects/{id},GET /projects/{id}/activities,GET /projects/{id}/employees,GET /projects/{id}/invoice-items,GET /projects/{id}/locations,GET /projects/{id}/price-tables,GET /projects/{id}/price-tables/{price_table_id},GET /projects/{id}/tasks,DELETE /projects/{id}/tasks/{task_id},GET /projects/{id}/tasks/{task_id},PATCH /projects/{id}/tasks/{task_id},PUT /projects/{id}/tasks/{task_id},GET /projects/{id}/tasks/{task_id}/employees,GET /projects/{id}/tasks/{task_id}/employees/{project_task_employee_id},GET /receipts,POST /receipts,GET /receipts-amounts,DELETE /receipts/{id},GET /receipts/{id},PATCH /receipts/{id},PUT /receipts/{id},GET /receipts/{id}/amounts,GET /receipts/{id}/amounts/{amount_id},DELETE /receipts/{id}/attachments,GET /receipts/{id}/attachments,PUT /receipts/{id}/attachments,GET /resource-plannings,GET /tickets,POST /tickets,DELETE /tickets/{id},GET /tickets/{id},PATCH /tickets/{id},PUT /tickets/{id},POST /tickets/{id}/attachments,GET /tickets/{id}/subtasks,POST /tickets/{id}/subtasks,DELETE /tickets/{id}/subtasks/{subtask_id},GET /tickets/{id}/subtasks/{subtask_id},PATCH /tickets/{id}/subtasks/{subtask_id},PUT /tickets/{id}/subtasks/{subtask_id},GET /vehicles,GET /workhour-balances,GET /workhour-balances/{id}: the response headerx-ratelimit-remainingwas added for the status200 - NEU
GET /absence-reasons,GET /absence-reasons/{name},GET /absences,POST /absences,GET /absences/{id},GET /activities,GET /activities/{activity},GET /articles,GET /articles/{id},GET /attendances,POST /attendances,DELETE /attendances/{id},GET /attendances/{id},PATCH /attendances/{id},PUT /attendances/{id},GET /categories,GET /categories/{name},GET /customers,POST /customers,DELETE /customers/{customer_number},GET /customers/{customer_number},PATCH /customers/{customer_number},PUT /customers/{customer_number},GET /customers/{customer_number}/contacts,POST /customers/{customer_number}/contacts,DELETE /customers/{customer_number}/contacts/{id},GET /customers/{customer_number}/contacts/{id},PATCH /customers/{customer_number}/contacts/{id},PUT /customers/{customer_number}/contacts/{id},GET /customers/{customer_number}/price-tables,GET /customers/{customer_number}/price-tables/{id},GET /departments,GET /departments/{id},GET /departments/{id}/children,GET /departments/{id}/employees,GET /devices,GET /devices/{id},GET /dynamic-attributes,GET /dynamic-attributes/{id},GET /employees,POST /employees,GET /employees/{username},PATCH /employees/{username},PUT /employees/{username},GET /employees/{username}/absences,GET /employees/{username}/employment-periods,POST /employees/{username}/employment-periods,GET /employees/{username}/employment-periods/{id},PATCH /employees/{username}/employment-periods/{id},PUT /employees/{username}/employment-periods/{id},GET /employees/{username}/internal-rates,GET /employees/{username}/internal-rates/{id},GET /employees/{username}/meals,GET /employees/{username}/meals/{id},GET /employees/{username}/projects,GET /employees/{username}/regular-working-times,POST /employees/{username}/regular-working-times,GET /employees/{username}/regular-working-times/{id},PATCH /employees/{username}/regular-working-times/{id},PUT /employees/{username}/regular-working-times/{id},GET /employees/{username}/transponders,GET /folders,GET /folders/{id},GET /folders/{id}/documents,GET /folders/{id}/documents/{document_id},GET /invoice-items,GET /invoice-items/{id},GET /invoices,GET /invoices/{invoice_number},GET /invoices/{invoice_number}/attachments,GET /invoices/{invoice_number}/items,GET /location-lists,GET /location-lists/{id},GET /location-lists/{id}/locations,GET /locations,GET /locations/{name},GET /offers,GET /offers/{id},GET /offers/{id}/items,GET /offers/{id}/items/{item_id},GET /planning,GET /plannings,GET /price-groups,GET /price-groups/{id},GET /projects,POST /projects,DELETE /projects/{id},GET /projects/{id},PATCH /projects/{id},PUT /projects/{id},GET /projects/{id}/activities,GET /projects/{id}/employees,GET /projects/{id}/invoice-items,GET /projects/{id}/locations,GET /projects/{id}/price-tables,GET /projects/{id}/price-tables/{price_table_id},GET /projects/{id}/tasks,POST /projects/{id}/tasks,DELETE /projects/{id}/tasks/{task_id},GET /projects/{id}/tasks/{task_id},PATCH /projects/{id}/tasks/{task_id},PUT /projects/{id}/tasks/{task_id},GET /projects/{id}/tasks/{task_id}/employees,GET /projects/{id}/tasks/{task_id}/employees/{project_task_employee_id},GET /receipts,POST /receipts,GET /receipts-amounts,DELETE /receipts/{id},GET /receipts/{id},PATCH /receipts/{id},PUT /receipts/{id},GET /receipts/{id}/amounts,GET /receipts/{id}/amounts/{amount_id},DELETE /receipts/{id}/attachments,GET /receipts/{id}/attachments,PUT /receipts/{id}/attachments,GET /resource-plannings,GET /tickets,POST /tickets,DELETE /tickets/{id},GET /tickets/{id},PATCH /tickets/{id},PUT /tickets/{id},POST /tickets/{id}/attachments,GET /tickets/{id}/subtasks,POST /tickets/{id}/subtasks,DELETE /tickets/{id}/subtasks/{subtask_id},GET /tickets/{id}/subtasks/{subtask_id},PATCH /tickets/{id}/subtasks/{subtask_id},PUT /tickets/{id}/subtasks/{subtask_id},GET /vehicles,GET /workhour-balances,GET /workhour-balances/{id}: added the non-success response with the status429 - INFO
POST /absences: removedsubschema #1, subschema #2from the response bodyanyOflist for the response status201 - INFO
POST /absences: the response’s bodytypewas narrowed fromanytoobjectfor status201 - NEU
POST /absences,POST /attendances,POST /customers,POST /customers/{customer_number}/contacts,POST /employees,POST /employees/{username}/employment-periods,POST /employees/{username}/regular-working-times,POST /projects,POST /projects/{id}/tasks: the response headerx-ratelimit-limitwas added for the status201 - NEU
POST /absences,POST /attendances,POST /customers,POST /customers/{customer_number}/contacts,POST /employees,POST /employees/{username}/employment-periods,POST /employees/{username}/regular-working-times,POST /projects,POST /projects/{id}/tasks: the response headerx-ratelimit-remainingwas added for the status201 - NEU
POST /absences: added the non-success response with the status502 - NEU
POST /absences: added the optional propertycodeto the response with the422status - NEU
POST /absences: added the optional propertystatusto the response with the422status - NEU
POST /absences: added the required propertydatato the response with the201status - INFO
GET /attendances: thedata/items/end_dateresponse’s propertyformatwas narrowed fromnonetodatefor status200 - INFO
POST /attendances: thedata/end_dateresponse’s propertyformatwas narrowed fromnonetodatefor status201 - INFO
GET /attendances/{id},PATCH /attendances/{id},PUT /attendances/{id}: thedata/end_dateresponse’s propertyformatwas narrowed fromnonetodatefor status200 - INFO
GET /departments,GET /departments/{id}/children: thedata/items/idresponse’s propertytypewas narrowed fromanytointegerfor status200 - INFO
GET /departments/{id}: thedata/idresponse’s propertytypewas narrowed fromanytointegerfor status200 - NEU
GET /departments/{id}/employees,GET /employees: added the optional propertydata/items/employment_periodsto the response with the200status - NEU
GET /departments/{id}/employees,GET /employees: added the optional propertydata/items/internal_ratesto the response with the200status - NEU
GET /departments/{id}/employees,GET /employees: added the optional propertydata/items/regular_working_timesto the response with the200status - INFO
GET /departments/{id}/employees,GET /employees: removed the “ enum value from thedata/items/salutation/nameresponse property for the response status200 - INFO
GET /departments/{id}/employees,GET /employees: removed theFrauenum value from thedata/items/salutation/nameresponse property for the response status200 - INFO
GET /departments/{id}/employees,GET /employees: removed theHerrenum value from thedata/items/salutation/nameresponse property for the response status200 - NEU
GET /departments/{id}/employees,GET /employees: added the required propertydata/items/cost_centerto the response with the200status - NEU
GET /departments/{id}/employees,GET /employees,GET /employees/{username}/projects,GET /projects/{id}/employees: added the required propertydata/items/noteto the response with the200status - NEU
GET /departments/{id}/employees,GET /employees: added the required propertydata/items/overtime_adsto the response with the200status - NEU
GET /employees,GET /employees/{username},GET /invoices: added the new optionalqueryrequest parameterinclude - NEU
POST /employees: added the optional propertydata/allOf[subschema #1: EmployeeResource]/employment_periodsto the response with the201status - NEU
POST /employees: added the optional propertydata/allOf[subschema #1: EmployeeResource]/internal_ratesto the response with the201status - NEU
POST /employees: added the optional propertydata/allOf[subschema #1: EmployeeResource]/regular_working_timesto the response with the201status - INFO
POST /employees: removed the “ enum value from thedata/allOf[subschema #1: EmployeeResource]/salutation/nameresponse property for the response status201 - INFO
POST /employees: removed theFrauenum value from thedata/allOf[subschema #1: EmployeeResource]/salutation/nameresponse property for the response status201 - INFO
POST /employees: removed theHerrenum value from thedata/allOf[subschema #1: EmployeeResource]/salutation/nameresponse property for the response status201 - NEU
POST /employees: added the required propertydata/allOf[subschema #1: EmployeeResource]/cost_centerto the response with the201status - NEU
POST /employees: added the required propertydata/allOf[subschema #1: EmployeeResource]/noteto the response with the201status - NEU
POST /employees: added the required propertydata/allOf[subschema #1: EmployeeResource]/overtime_adsto the response with the201status - NEU
GET /employees/{username},GET /employees/{username}/employment-periods,GET /employees/{username}/internal-rates,GET /employees/{username}/regular-working-times: added the non-success response with the status422 - NEU
GET /employees/{username}: added the optional propertydata/employment_periodsto the response with the200status - NEU
GET /employees/{username}: added the optional propertydata/internal_ratesto the response with the200status - NEU
GET /employees/{username}: added the optional propertydata/regular_working_timesto the response with the200status - INFO
GET /employees/{username}: removed the “ enum value from thedata/salutation/nameresponse property for the response status200 - INFO
GET /employees/{username}: removed theFrauenum value from thedata/salutation/nameresponse property for the response status200 - INFO
GET /employees/{username}: removed theHerrenum value from thedata/salutation/nameresponse property for the response status200 - NEU
GET /employees/{username}: added the required propertydata/cost_centerto the response with the200status - NEU
GET /employees/{username}: added the required propertydata/noteto the response with the200status - NEU
GET /employees/{username}: added the required propertydata/overtime_adsto the response with the200status - NEU
PATCH /employees/{username},PUT /employees/{username}: added the optional propertydata/allOf[subschema #1: EmployeeResource]/employment_periodsto the response with the200status - NEU
PATCH /employees/{username},PUT /employees/{username}: added the optional propertydata/allOf[subschema #1: EmployeeResource]/internal_ratesto the response with the200status - NEU
PATCH /employees/{username},PUT /employees/{username}: added the optional propertydata/allOf[subschema #1: EmployeeResource]/regular_working_timesto the response with the200status - INFO
PATCH /employees/{username},PUT /employees/{username}: removed the “ enum value from thedata/allOf[subschema #1: EmployeeResource]/salutation/nameresponse property for the response status200 - INFO
PATCH /employees/{username},PUT /employees/{username}: removed theFrauenum value from thedata/allOf[subschema #1: EmployeeResource]/salutation/nameresponse property for the response status200 - INFO
PATCH /employees/{username},PUT /employees/{username}: removed theHerrenum value from thedata/allOf[subschema #1: EmployeeResource]/salutation/nameresponse property for the response status200 - NEU
PATCH /employees/{username},PUT /employees/{username}: added the required propertydata/allOf[subschema #1: EmployeeResource]/cost_centerto the response with the200status - NEU
PATCH /employees/{username},PUT /employees/{username}: added the required propertydata/allOf[subschema #1: EmployeeResource]/noteto the response with the200status - NEU
PATCH /employees/{username},PUT /employees/{username}: added the required propertydata/allOf[subschema #1: EmployeeResource]/overtime_adsto the response with the200status - NEU
GET /employees/{username}/employment-periods,GET /employees/{username}/internal-rates,GET /employees/{username}/regular-working-times: added the new optionalqueryrequest parameterlimit - INFO
GET /employees/{username}/projects,GET /folders/{id}/documents,GET /projects/{id}/employees: removedsubschema #1, subschema #2from thedata/items/response propertyanyOflist for the response status200 - INFO
GET /employees/{username}/projects,GET /folders/{id}/documents,GET /projects/{id}/employees: thedata/items/response’s propertytypewas narrowed fromanytoobjectfor status200 - NEU
GET /employees/{username}/projects,GET /projects/{id}/employees: added the required propertydata/items/availabilityto the response with the200status - NEU
GET /employees/{username}/projects,GET /projects/{id}/employees: added the required propertydata/items/employee_idto the response with the200status - NEU
GET /employees/{username}/projects,GET /projects/{id}/employees: added the required propertydata/items/fromto the response with the200status - NEU
GET /employees/{username}/projects,GET /folders/{id}/documents,GET /projects/{id}/employees: added the required propertydata/items/idto the response with the200status - NEU
GET /employees/{username}/projects,GET /projects/{id}/employees: added the required propertydata/items/internal_rateto the response with the200status - NEU
GET /employees/{username}/projects,GET /projects/{id}/employees: added the required propertydata/items/priceGroupto the response with the200status - NEU
GET /employees/{username}/projects,GET /projects/{id}/employees: added the required propertydata/items/project_idto the response with the200status - NEU
GET /employees/{username}/projects,GET /projects/{id}/employees: added the required propertydata/items/project_numberto the response with the200status - NEU
GET /employees/{username}/projects,GET /projects/{id}/employees: added the required propertydata/items/rate_typeto the response with the200status - NEU
GET /employees/{username}/projects,GET /projects/{id}/employees: added the required propertydata/items/toto the response with the200status - NEU
GET /employees/{username}/projects,GET /projects/{id}/employees: added the required propertydata/items/typeto the response with the200status - NEU
GET /folders/{id}/documents: added the required propertydata/items/file_contentsto the response with the200status - NEU
GET /folders/{id}/documents: added the required propertydata/items/file_typeto the response with the200status - NEU
GET /folders/{id}/documents: added the required propertydata/items/filenameto the response with the200status - INFO
GET /folders/{id}/documents/{document_id}: removedsubschema #1, subschema #2from thedataresponse propertyanyOflist for the response status200 - INFO
GET /folders/{id}/documents/{document_id}: thedataresponse’s propertytypewas narrowed fromanytoobjectfor status200 - NEU
GET /folders/{id}/documents/{document_id}: added the required propertydata/file_contentsto the response with the200status - NEU
GET /folders/{id}/documents/{document_id}: added the required propertydata/file_typeto the response with the200status - NEU
GET /folders/{id}/documents/{document_id}: added the required propertydata/filenameto the response with the200status - NEU
GET /folders/{id}/documents/{document_id}: added the required propertydata/idto the response with the200status - NEU
GET /invoices: added the new optionalqueryrequest parameterend_date - NEU
GET /invoices: added the new optionalqueryrequest parametermodified_after - NEU
GET /invoices: added the new optionalqueryrequest parametermodified_before - NEU
GET /invoices: added the new optionalqueryrequest parameterstart_date - NEU
GET /invoices: added the optional propertydata/items/itemsto the response with the200status - NEU
GET /invoices/{invoice_number}: added the optional propertydata/itemsto the response with the200status - NEU
GET /plannings: added the optional propertydata/items/planning_details/effective_hoursto the response with the200status - NEU
GET /plannings: added the optional propertydata/items/planning_details/from_timeto the response with the200status - NEU
GET /plannings: added the optional propertydata/items/planning_details/to_timeto the response with the200status - INFO
GET /plannings: removedsubschema #1, subschema #2from thedata/items/planning_detailsresponse propertyanyOflist for the response status200 - INFO
GET /plannings: thedata/items/planning_detailsresponse’s propertytypewas narrowed fromanytoobjectfor status200 - NEU
GET /plannings: added the required propertydata/items/planning_details/typeto the response with the200status - NEU
GET /plannings: added the required propertydata/items/planning_details/unitto the response with the200status - NEU
GET /plannings: added the required propertydata/items/planning_details/valueto the response with the200status - NEU
GET /project-employees: endpoint added - INFO
GET /projects: thedata/items/keywords/items/response’s propertytypewas narrowed fromanytostringfor status200 - INFO
POST /projects: thedata/allOf[subschema #1: ProjectResource]/keywords/items/response’s propertytypewas narrowed fromanytostringfor status201 - INFO
GET /projects/{id}: thedata/keywords/items/response’s propertytypewas narrowed fromanytostringfor status200 - INFO
PATCH /projects/{id},PUT /projects/{id}: thedata/allOf[subschema #1: ProjectResource]/keywords/items/response’s propertytypewas narrowed fromanytostringfor status200 - NEU
GET /projects/{id}/tasks: added the required propertydata/items/all_project_employeesto the response with the200status - NEU
POST /projects/{id}/tasks: added the required propertydata/allOf[subschema #1: ProjectTaskResource]/all_project_employeesto the response with the201status - NEU
GET /projects/{id}/tasks/{task_id}: added the required propertydata/all_project_employeesto the response with the200status - NEU
PATCH /projects/{id}/tasks/{task_id},PUT /projects/{id}/tasks/{task_id}: added the required propertydata/allOf[subschema #1: ProjectTaskResource]/all_project_employeesto the response with the200status - INFO
GET /projects/{id}/tasks/{task_id}/employees: thedata/items/fromresponse’s propertyformatwas narrowed fromnonetodatefor status200 - INFO
GET /projects/{id}/tasks/{task_id}/employees: thedata/items/toresponse’s propertyformatwas narrowed fromnonetodatefor status200 - INFO
GET /projects/{id}/tasks/{task_id}/employees/{project_task_employee_id}: thedata/fromresponse’s propertyformatwas narrowed fromnonetodatefor status200 - INFO
GET /projects/{id}/tasks/{task_id}/employees/{project_task_employee_id}: thedata/toresponse’s propertyformatwas narrowed fromnonetodatefor status200
v8.0.27 · OpenAPI 1.0.0 · 22.10.2026
Search combines with the other filters: GET /projects, GET /projects/{id}/tasks
search matches the name or the description, and both now apply together with the other filters
(project, status, …). Before, a match on the description or name bypassed them, so
GET /projects/12/tasks?search=meeting also returned tasks of other projects. No contract change.
v8.0.24 · OpenAPI 1.0.0 · 22.10.2026
Contacts without primary: POST /customers/{customer_number}/contacts, PUT/PATCH /customers/{customer_number}/contacts/{id}
Creating a contact with primary set to null answered 500. A contact created without
primary — null or left out — now becomes the customer’s primary contact when the customer has
none yet, and a secondary one otherwise, as in ZEP; before, a left-out primary was stored empty
and read back as null. Updating with primary: null keeps the contact’s current value instead of
clearing it. No contract change.
Error body when ZEP rejects a write: POST /receipts, PUT/PATCH /receipts/{id}, POST /tickets, PUT/PATCH /tickets/{id}, POST /tickets/{id}/subtasks, PUT/PATCH /tickets/{id}/subtasks/{subtask_id}
When ZEP rejected the write without an error text, these endpoints answered a bare 500 instead
of the documented {"success": false, "message": …}. They now always answer with that body:
message is ZEP’s text, or “The request could not be processed.” when ZEP sent none. The status
stays 500. No contract change.
Project billability and billing type on update: PUT/PATCH /projects/{project}
default_billability: null answered 500. It now keeps the project’s current default
billability, the same as leaving the field out. An unknown value (99, "foo", {"id": 99}) for
default_billability also answered 500; for billing_type and receipts_billability it was
accepted and cleared the field. All three now reject it with 422 and a validation error on the
field. No contract change.
v8.0.17 · OpenAPI 1.0.0 · 13.10.2026
Fahrzeuge: GET /vehicles
Listet die Fahrzeuge einer Fahrzeugliste; die id ist das Kürzel, das Projektzeiten als vehicle_id tragen. Fahrzeuge liegen in datierten Listen; gültig ist an date (YYYY-MM-DD, Standard heute) die Liste mit dem spätesten Startdatum an oder vor diesem Tag. Vor der ersten Liste ist die Antwort leer. Paginiert über limit, nur lesend. Auf ZEP Clock nicht verfügbar.
Rechnungsersteller: GET /invoices, GET /invoices/{invoice}
Jede Rechnung trägt jetzt creator_username, den Benutzernamen des Mitarbeiters, der das Rechnungsdokument erstellt hat – der Mitarbeiter, den ZEP auf der Rechnung zeigt. Nach einer Korrektur ist es der Mitarbeiter, der die Rechnung korrigiert hat; null, wenn kein Rechnungsdokument gespeichert ist.
Projektzeiten akzeptieren das Fahrzeug-Kürzel: POST /attendances, PATCH /attendances/{id}
vehicle_id ist das Kürzel des Fahrzeugs (so wie GET es schon liefert, z. B. PKW), ein String mit höchstens 32 Zeichen. Bisher wurde es mit „must be a number“ abgelehnt; eine Zahl hat nur bei numerischen Kürzeln funktioniert und tut das weiterhin (sie wird als Kürzel gelesen). Bitte das Kürzel als String senden.
Die Typänderung von vehicle_id ist als Korrektur geführt, nicht als brechende Änderung: Die Anfrage hat vorher gar kein Fahrzeug angenommen (jeder Wert wurde abgelehnt), daher hängt keine funktionierende Integration am alten Typ. Eine Zahl, die einem numerischen Kürzel entspricht, wird weiterhin angenommen.
Alle Einzeländerungen (5)
- KORREKTUR
POST /attendances,PATCH /attendances/{id},PUT /attendances/{id}: thevehicle_idrequest propertytypechanged fromnumber, nulltostring, null - INFO
POST /attendances,PATCH /attendances/{id},PUT /attendances/{id}: thevehicle_idrequest property’s maxLength was set to32 - NEU
GET /invoices: added the required propertydata/items/creator_usernameto the response with the200status - NEU
GET /invoices/{invoice_number}: added the required propertydata/creator_usernameto the response with the200status - NEU
GET /vehicles: endpoint added
v8.0.15 · OpenAPI 1.0.0 · 12.10.2026
Task description limited to 255 characters: POST /projects/{id}/tasks, PUT/PATCH /projects/{id}/tasks/{task_id}
A description longer than 255 characters answered 500, because the task’s description holds
at most 255. It is now rejected with 422 and a validation error on description, and the
request schema declares maxLength: 255.
Alle Einzeländerungen (1)
- INFO
POST /projects/{id}/tasks,PATCH /projects/{id}/tasks/{task_id},PUT /projects/{id}/tasks/{task_id}: thedescriptionrequest property’s maxLength was set to255
v8.0.11 · OpenAPI 1.0.0 · 10.10.2026
List endpoints sort by orderBy and order
Most list endpoints accepted both parameters and ignored them, so GET /offers?orderBy=status
came back in id order. Every list endpoint that documents them now sorts by the record’s key, its
writable fields or created/modified, named as in the response (status, name,
valid_until, …), order asc or desc. Records with the same value follow in key order
(resource plannings: start date, employee, id), so paging stays stable, and links keep the
sort. Any other orderBy value sorts by the default, the key (name for locations, the start
date for resource plannings); before, it answered 500 on the location, location list and
dynamic attribute definition lists. GET /plannings keeps its date order, and
GET /projects/{project}/employees its own. No contract change.
v8.0.10 · OpenAPI 1.0.0 · 06.10.2026
GET /resource-plannings liefert die Planungen der Ressourcenplanung: Zeitraum, Einheit (percent oder hours pro Tag) und Wert, dazu die geplanten Stunden je Kalendermonat und gesamt. Filterbar nach date_from/date_to, employee_ids[], project_ids[], customer_numbers[] und modified_after/modified_before. Nur mit dem Modul Ressourcenplanung (MOD_PLANNING_NEXT oder Produktlinie 4 ohne MOD_RESSOURCENPLANUNG); andere Mandanten erhalten 404. GET /plannings bleibt unverändert.
Alle Einzeländerungen (1)
- NEU
GET /resource-plannings: endpoint added
v8.0.9 · OpenAPI 1.0.0 · 06.10.2026
Die REST-API steht auf jeder ZEP-Produktlinie (Produktwelt 2026) ohne das separate Modul MOD_SOAP zur Verfügung; Mandanten mit Produktlinie erhielten vorher 404. Lizenzen ohne Produktlinie brauchen weiterhin MOD_SOAP. Der Vertrag selbst hat sich nicht geändert.
v8.0.7 · OpenAPI 1.0.0 · 25.09.2026 — die generierte Spezifikation wird maßgeblich
- INFO Das OpenAPI-Dokument wird bei jedem Release aus dem Code (Controller, Form Requests, Resources) erzeugt und hier unverändert veröffentlicht — dieselbe Datei, die die interaktive Referenz rendert. Beschreibungen stehen neben dem Feld, das sie beschreiben, und können nicht mehr auseinanderlaufen.
- INFO Versionsschema:
info.versionist1.0.0und folgt ab jetzt dem Vertrag. Die bisherigen Nummern (7.12.49und älter) spiegelten die ZEP-Produktversion. - INFO Alle Referenzseiten dieser Dokumentation (und diese Navigation) werden aus der Spezifikation erzeugt, auf Deutsch und Englisch. Die Antwortbeispiele sind die Feldbeispiele der Spezifikation.
- INFO Für ZEP Clock wird eine eigene Spezifikation neben der Vollversion veröffentlicht; die Referenzseite hat einen Umschalter, und jede Endpunktseite sagt, ob der Endpunkt auf einer Clock-Instanz verfügbar ist.
- NEU Alle Update-Operationen akzeptieren
PUTals Alias vonPATCH— der Router tat das immer, die Dokumentation sagt es jetzt. - ABGEKÜNDIGT GET Planung (Legacy-Alias):
GET /planningleitet aufGET /planningsum und antwortet mit den HeadernDeprecation,Sunset(2027-08-31) undLink. Der Alias wird frühestens am 2027-08-31 entfernt. - ABGEKÜNDIGT Kunden: das Objekt
statusträgt in der Spezifikationdeprecated: trueundx-sunset: 2027-08-31. Lesen Sie stattdesseninactive;statusist nicht Teil von API v2. - FIX Strengere Eingabeprüfung für Anfragen, die vorher kein brauchbares Ergebnis geliefert haben:
POST /attendances,PATCH /attendances/{id}undPUT /attendances/{id}nehmendatenur noch alsJJJJ-MM-TT(eine Uhrzeit endete in einem Serverfehler, 500);GET /departmentslehnt ein leeresstart_dateoderend_dateab (das ergab nie einen korrekten Filter). Beides antwortet jetzt mit422; Clients, die bereits reine Datumswerte senden, sind nicht betroffen. - FIX Pfadparameter heißen nach dem Schlüssel, über den die API auflöst:
/absence-reasons/{name},/activities/{activity},/categories/{name},/customers/{customer_number}/contacts/{id},/invoices/{invoice_number}. Numerische IDs haben dort nie funktioniert; die Dokumentation behauptete es. - FIX
createdundmodifiedsind an jeder Ressource dokumentiert stattcreated_at/updated_at, die die API nie geliefert hat. - FIX Regelarbeitszeiten liefern
holidayCalendarundbreakRegulationTypemit den englischen Feldnamen, die die API immer zurückgegeben hat (name,note,country,region), nicht mit den deutschen Spaltennamen des alten Dokuments. - FIX Nullability und Pflichtfelder jeder Antwort entsprechen den echten Antworten (an einer laufenden Instanz gemessen), und der Filter
status[]der Rechnungspositions-Endpunkte deklariert seine Werte als Ganzzahlen.
v7.12.49 (24.07.2026)
Fasst alle Änderungen seit v7.12 (22.06.2026) zusammen.
- FIX POST Projektzeit anlegen: Die Buchungsregeln werden jetzt vor dem Schreiben geprüft, Verstöße kommen als
422mit Meldungen je Feld zurück — abgeschlossener Monat, überschrittenes Freigabedatum, Erfassungszeitraum, Nichtarbeitstag oder Feiertag, Beschäftigungszeitraum, Laufzeit von Projekt, Vorgang oder Ticket, überschrittene Planstunden. Die Schnittstelle hat solche Buchungen bisher als einzige angenommen; Weboberfläche, SOAP und Clock lehnen sie seit jeher ab. Wer gültig bucht, merkt nichts. - FIX Projektzeiten: Eine Buchung, die um
00:00endet, gehört zum Folgetag. Diese Grenze geht jetzt in die Überschneidungsprüfung ein, Doppelbuchungen über die Tagesgrenze werden dadurch erkannt und abgelehnt. - FIX GET Kunden:
statusbleibt erhalten und meldet jetzt den tatsächlichen Zustand. Zuvor kam ein inaktiver Kunde als{ "value": 1, "label": "Active" }zurück, obwohl die Dokumentation immer1 = aktivsagte. Für aktive Kunden ändert sich nichts; wer die Umkehrung im eigenen Client ausgeglichen hat, entfernt diesen Ausgleich. - VERALTET Kunden:
inactive(boolean) ersetztstatus.statuswird weiterhin ausgeliefert und beiPOSTundPATCHakzeptiert; werden beide gesendet, gewinntinactive. Der Filter?status=ist ebenfalls veraltet, Ersatz ist?inactive=. Ein Abkündigungsdatum wird gesondert angekündigt. - NEU PATCH Projektzeit aktualisieren
- NEU DELETE Projektzeit löschen: Prüft die Sperrfristen — abgeschlossener Monat, Freigabedatum, Erfassungszeitraum
- NEU POST Ansprechpartner anlegen
- NEU GET Ansprechpartner abrufen
- NEU PATCH Ansprechpartner aktualisieren
- NEU DELETE Ansprechpartner löschen
- NEU PATCH Projektvorgang aktualisieren
- NEU Projektzeiten:
end_datein jeder Antwort — der Kalendertag, an dem die Buchung endet.toallein ist bei einer Buchung bis Tagesende mehrdeutig, weil sie alsto = 00:00:00am Starttag gespeichert wird. - NEU Ansprechpartner:
departmentundcategoriesin der Antwort - NEU Die Filter
modified_afterundmodified_beforeauf mehreren Listen-Endpunkten - NEU Die Sprachen
sk(Slowakisch) undtr(Türkisch) - FIX Ein leerer Datumsfilter liefert
422statt500— bei Abwesenheiten und fünf weiteren Endpunkten - FIX Mitarbeiter, deren Beschäftigungszeitraum kein Anfangs- oder Enddatum hat, werden nicht mehr als „außerhalb des Beschäftigungszeitraums” abgelehnt. Eine fehlende Grenze bedeutet jetzt offen.
- FIX
default_billabilityundreceipts_billabilitywerden beiPOSTundPATCHnicht mehr ignoriert - FIX Planung: Die konfigurierten Vorgabewerte werden zurückgegeben
- INFO Neun Operationen, die es längst gab, sind jetzt beschrieben — unter anderem
POST /absences,GET /attendances/:idundPATCH /employees/:username. Dazu 66 Felder, die Antworten schon immer enthielten, und zwei Beziehungsschlüssel bei den regulären Arbeitszeiten, die nie so hießen wie dokumentiert.
v7.12 (22.06.2026)
- NEU GET Arbeitszeitsalden: Endpunkt zum Abrufen der zwischengespeicherten Überstunden- und Urlaubsansprüche hinzugefügt
- NEU GET Arbeitszeitsaldo-Details: Endpunkt für einen einzelnen Arbeitszeitsaldo-Datensatz hinzugefügt
- NEU GET Rechnungspositionen nach Projekt: Endpunkt zum Auflisten der Rechnungspositionen eines Projekts hinzugefügt
v7.8.74 (09.01.2026)
- NEU POST Ticket erstellen: Endpunkt zum Erstellen von Tickets hinzugefügt
- NEU PATCH Ticket aktualisieren: Endpunkt zum Aktualisieren von Tickets hinzugefügt
- NEU DELETE Ticket löschen: Endpunkt zum Löschen von Tickets hinzugefügt
- NEU POST Ticket-Anhang hochladen: Endpunkt zum Hochladen von Ticket-Anhängen hinzugefügt
- NEU POST Teilaufgabe erstellen: Endpunkt zum Erstellen von Teilaufgaben hinzugefügt
- NEU PATCH Teilaufgabe aktualisieren: Endpunkt zum Aktualisieren von Teilaufgaben hinzugefügt
- NEU DELETE Teilaufgabe löschen: Endpunkt zum Löschen von Teilaufgaben hinzugefügt
- NEU PATCH Kunde aktualisieren: Endpunkt zum Aktualisieren von Kunden hinzugefügt
- NEU DELETE Kunde löschen: Endpunkt zum Löschen von Kunden hinzugefügt
- NEU PATCH Projekt aktualisieren: Endpunkt zum Aktualisieren von Projekten hinzugefügt
- NEU DELETE Projekt löschen: Endpunkt zum Löschen von Projekten hinzugefügt
v7.8.64 (07.01.2026)
- NEU POST Beleg erstellen: Endpunkt zum Erstellen von Belegen hinzugefügt
- NEU PATCH Beleg aktualisieren: Endpunkt zum Aktualisieren von Belegen hinzugefügt
- NEU DELETE Beleg löschen: Endpunkt zum Löschen von Belegen hinzugefügt
- NEU PUT Anhang hochladen: Endpunkt zum Hochladen von Beleganhängen hinzugefügt
- NEU DELETE Anhang löschen: Endpunkt zum Löschen von Beleganhängen hinzugefügt
v7.7.0 (07.10.2025)
- NEU GET Artikel: Endpunkt zum Abrufen von Artikeln hinzugefügt
- NEU GET Artikel-Details: Endpunkt für einzelnen Artikel hinzugefügt
- NEU GET Planung: Endpunkt zum Abrufen von Planungseinträgen hinzugefügt
- NEU GET Projekt-Standorte: Endpunkt für Standorte eines Projekts hinzugefügt
- NEU PUT Mitarbeiter aktualisieren: Endpunkt zum Aktualisieren von Mitarbeitern hinzugefügt
- NEU PUT Beschäftigungszeitraum aktualisieren: Endpunkt zum Aktualisieren von Beschäftigungszeiträumen hinzugefügt
- FIX Korrektur Datentyp
plan_can_exceedvon boolean zu enum (0-4) in allen Projekt-Endpunkten - FIX Fehlende Filterparameter (
project_status,project_start_date,project_end_date) für Mitarbeiterprojekte hinzugefügt - FIX Fehlendes
idFeld in ProjectEmployee Response-Beispielen ergänzt
v7.6.1 (22.09.2025)
- NEU GET Standorte: Endpunkt zum Abrufen von Standorten hinzugefügt
- NEU GET Standortdetails: Endpunkt für einzelnen Standort hinzugefügt
- NEU GET Standortlisten: Endpunkt für Standortlisten hinzugefügt
- NEU GET Standortlisten-Details: Endpunkt für einzelne Standortliste hinzugefügt
- NEU GET Standorte in Liste: Endpunkt für Standorte innerhalb einer Liste hinzugefügt
- NEU GET Dynamische Attribute: Endpunkt für dynamische Attributdefinitionen hinzugefügt
- NEU GET Details dynamischer Attribute: Endpunkt für einzelnes dynamisches Attribut hinzugefügt
- NEU GET Rechnungspositionen: Separater Endpunkt für alle Rechnungspositionen hinzugefügt
- NEU GET Rechnungspositionsdetails: Endpunkt für einzelne Rechnungsposition hinzugefügt
- NEU GET Rechnungsanhänge: Endpunkt für Rechnungsanhänge hinzugefügt
- NEU POST Abwesenheit erstellen: Endpunkt zum Erstellen von Abwesenheiten hinzugefügt
- NEU POST Beschäftigungszeitraum erstellen: Endpunkt zum Erstellen von Beschäftigungszeiträumen hinzugefügt
- NEU POST Kunden erstellen: Endpunkt zum Erstellen von Kunden hinzugefügt
- NEU POST Projekt erstellen: Endpunkt zum Erstellen von Projekten hinzugefügt
- NEU POST Projektaufgabe erstellen: Endpunkt zum Erstellen von Projektaufgaben hinzugefügt
- INFO OpenAPI-Spezifikation auf ~85 Endpunkte erweitert (vorher ~35)
- INFO Response-Beispiele für einige neue Endpunkte werden im nächsten Update ergänzt
v7.4.2 (24.06.2025)
- NEU GET Projektzeiten: Neue Felder
project_release,project_released_atundproject_released_byfür die Projektzeitfreigabe hinzugefügt - NEU GET Projektzeit-Details: Neue Felder für die Projektzeitfreigabe hinzugefügt
v7.4.1 (18.06.2025)
- INFO OpenAPI-Spezifikation aktualisiert, um alle dokumentierten Endpunkte einzuschließen
- NEU POST Mitarbeiter erstellen
- FIX GET Abwesenheiten: Response-Beispiele an aktuelle API-Struktur angepasst, inklusive neuer Felder
absence_reason_id,absenceReasonObjekt und korrigierte Datumsformate - FIX GET Projektzeiten: Response-Beispiele an aktuelle API-Struktur angepasst, Datums-/Zeitformate aktualisiert
- NEU GET Projektzeit-Details: Dokumentation für einzelne Projektzeit hinzugefügt
- FIX GET Abteilungen: Response-Beispiele mit neuen Feldern
parent_id,manager_count,children_countaktualisiert - FIX GET Abteilungs-Details: Response-Struktur mit
managerArray stattmanager_countaktualisiert - FIX GET Abteilungs-Mitarbeiter: Response-Beispiele mit neuem Feld
approval_dateund Language-Objektstruktur aktualisiert - FIX GET Mitarbeiter: Neue Felder
categoriesunddynamicAttributesArrays hinzugefügt,release_datezuapproval_dategeändert - NEU GET Mitarbeiter-Transponder: Dokumentation für das Abrufen von RFID-Transpondern eines Mitarbeiters hinzugefügt
- FIX GET Terminals: Response-Beispiele an aktuelle API-Struktur mit Gerätetypen und Statuscodes angepasst
- FIX GET Terminal-Details: Response-Struktur mit employees und categories Arrays aktualisiert
- FIX GET Kunden: Response-Struktur mit neuen Feldern
vat(ersetzttax),statusals Objekt,department_id,categoriesunddynamicAttributesArrays aktualisiert - FIX GET Kunden-Details: Response mit neuen Adressfeldern aktualisiert,
employeesArray entfernt, vollständigepriceTablesStruktur hinzugefügt - FIX GET Angebote: Response-Beispiele aktualisiert und fehlendes Feld
order_probability_percenthinzugefügt - NEU GET Angebotspositionen: Dokumentation für das Abrufen von Angebotspositionen hinzugefügt
- FIX GET Belege: Response-Struktur mit vereinfachter
receipt_type_idals String statt Objekt aktualisiert, Feldnamenis_amount_gross_or_netzuis_amount_netundis_invoice_amount_gross_or_netzuis_invoice_amount_netgeändert - FIX GET Beleg-Details: Response-Struktur an Listen-Endpunkt mit gleichen Feldänderungen angepasst
- FIX GET Beleg-Beträge: Response-Beispiele mit realistischen Daten aktualisiert und neue Felder
is_amount_netundis_invoice_amount_nethinzugefügt - FIX GET Beleg-Anhänge: Response-Beispiel mit Base64-codierten Dateiinhalten hinzugefügt
- FIX GET Tickets: Response-Beispiele korrigiert - enthielten fälschlicherweise Belegdaten statt Ticketdaten,
categoriesArray hinzugefügt - FIX GET Ticket-Details: Response-Beispiel mit aktuellen Daten aktualisiert
- FIX GET Ticket-Teilaufgaben: Response-Beispiel mit realistischen Daten aktualisiert
- FIX GET Ticket-Teilaufgabe Details: Response-Beispiel mit anonymisierten Daten aktualisiert, Überschrift korrigiert
- NEU GET Ordner: Neuer Endpunkt zum Abrufen von Ordnern hinzugefügt
- NEU GET Ordner-Dokumente: Neuer Endpunkt zum Abrufen von Dokumenten innerhalb eines Ordners
- NEU GET Preisgruppen: Neuer Endpunkt zum Abrufen von Preisgruppen hinzugefügt
- NEU GET Tätigkeiten: Neuer Endpunkt zum Abrufen von Tätigkeiten hinzugefügt
- NEU GET Fehlgründe: Neuer Endpunkt zum Abrufen von Fehlgründen hinzugefügt
- NEU GET Kategorien: Neuer Endpunkt zum Abrufen von Kategorien hinzugefügt
v7.4.0 (17.06.2025)
- NEU Hauptversion-Update mit erweiterten API-Funktionen
- INFO Aktualisierte API-Dokumentation und verbesserte Endpunkt-Beschreibungen
v6.13.17 (16.09.2024)
- INFO GET Projekt-Tasks wurden erweitert
v6.13.16 (27.08.2024)
- NEU GET Projekt-Preistabellen abrufen
- NEU GET Projektvorgang-Details abrufen
- NEU GET Unterabteilungen abrufen
- NEU GET Abteilungs-Mitarbeiter abrufen
- NEU GET Kunden-Preistabellen abrufen
- NEU GET Projekte eines Mitarbeiters abrufen
- NEU GET Rechnungen abrufen
- NEU GET Rechnungs-Details abrufen
- NEU GET Rechnungspositionen abrufen
- NEU GET Angebote abrufen
- NEU GET Angebot-Details abrufen
- FIX GET Projekte: Es wurden u.U. nicht alle Projekte angezeigt.
v6.12.5 (06.03.2024)
- INFO Das Rate-Limiting wurde auf 12000 Requests pro Stunde erhöht. Pro 5 Minuten sind maximal 1000 Requests erlaubt
- NEU GET Projekte Projektfilter um den Parameter
nameerweitert - FIX GET Projekte: Der Wert
plan_hourszeigte keine Planstunden, wenn diese in Vorgängen definiert wurden. Hierzu wurde nun der Wertplan_hours_childrenhinzugefügt - FIX GET Projekte: Das Projekt muss nun nicht mehr komplett innerhalb eines gefilterten Zeitraums liegen. Es reicht, wenn es innerhalb des Zeitraums aktiv war
- FIX GET Mitarbeiter: Unter bestimmten Umständen trat ein Server Error auf. Dieser Fehler wurde behoben
v6.12.4 (01.03.2024)
- INFO Kleinere Optimierung der Performance
v6.12.3 (16.02.2024)
- GET-Requests für Belege, Tickets
- Filterung der Projektzeiten nach
project_idundticket_idhinzugefügt - Zusatzattribute
dynamicAttributeszu Projektdetails und Mitarbeiterdetails hinzugefügt
v6.12.2 (02.02.2024)
- Bugfix für
GET /absences:absenceReasonwird nun korrekt zurückgegeben - GET-Requests für Abteilungen, Terminals
- GET-Request für Mitarbeiter-Mahlzeiten
v6.12.1 (14.01.2024)
- Initialer Release der ZEP REST-API
- GET-Requests für Projekte, Projektzeiten, Mitarbeiter, Kunden und Abwesenheiten