Skip to content

Lists, Filters, and References ​

These rules apply to the OAuth API. Status filters, references, pagination, and sorting apply to the list endpoints; date and time parameters apply across all endpoints — unless the specific endpoint's article says otherwise.

Status filters ​

The status parameter filters a list endpoint by the status of its records. The default value is all — without the parameter, the list returns every value. You can pass several values comma-separated, e.g. status=active,archived. An invalid value returns 400 validation_error.

EndpointValuesactive means
List Waresactive, archived, deletedneither archived nor deleted
List Coursesactive, archivednot archived
List Attendeesactive, deletednot canceled
List Todosactive, deletednot deleted
Get Statusesactive, deletednot deleted
List Storages, List Distributorsactive, deletednot deleted
List Dunning Levelsactive, deletednot deleted
List Dunningsactive, replaced, cancelednot deleted and dunning status active, done, or empty
List Customersactive, archived, deletedneither archived nor deleted
List Appointments, List Users, List Invoices, List Fee Notesactive, deletednot deleted / not canceled
Tags, references, ware categories, stock movements, purchases, customer groups, reports–no status parameter, see below

Dunnings add a second parameter: dunningStatus (active, replaced, canceled, done) filters by the business dunning status and combines with status using AND logic. For status, canceled there means: dunning status canceled or deleted — a deleted dunning is always canceled, regardless of its dunning status.

Endpoints without a status filter ​

The following lists have no status parameter, because their records carry no deletion timestamp or end state: tags, references, ware categories, stock movements, purchases, customer groups, and reports. They always return the full, unfiltered set.

Single fetch ​

Fetching a single record (GET /{resource}/{uuid}) always responds with 200 OK, even for a record in an end state — deleted, canceled, archived, or replaced. A record that is no longer active does not return a 404.

A single fetch does respond with 404 when the record is not reachable for the owning user of your API access: for invoices, fee notes, cash revenues, and dunnings when the record's tenancy is not accessible (see Access with the Tenancies add-on). In this case the response is exactly the same as for an unknown UUID, so it reveals nothing about whether the record exists:

json
{"error":"not_found","error_description":"The requested resource was not found."}

This does not apply to a course attendee: if the course is not readable for the owning user, the single fetch responds with 403 access_denied.

Deletion state in the response ​

Every record with a deletion state carries the field deleted (boolean). If the underlying entity has a deletion timestamp, it also carries deletedAt (ISO 8601, null if not deleted). Where a record can also be archived, it carries the field archived (boolean). Archived and deleted are independent states.

References to other records ​

A reference to another record comes as a UUID string, e.g. "customer": "<uuid>". A list of references comes as an array of UUID strings, e.g. "instructors": ["<uuid>"]. If the reference is missing, the value is null ([] for lists). These fields no longer contain embedded objects or names.

To load the data behind a reference, call its endpoint with the UUID. You need the same scope for that call as for the list the reference came from.

RelationEndpoint
customerGET /customers/{uuid}
courseGET /courses/{uuid}
invoiceGET /invoices/{uuid}
dunningTypeGET /invoices/dunnings/types/{uuid}
tenancy / tenanciesGET /tenancies/{uuid}
assignedTo, treatingUser, instructors, consumer, createdBy, updatedByGET /users/{uuid}
status (todos)GET /todos/statuses/{uuid}
tags (todos)GET /todos/tags/{uuid}
attendees (courses)GET /courses/{uuid}/attendees/{attendeeUuid}
category (courses, services)GET /services/categories/{uuid}
category (wares)GET /wares/categories/{uuid}
ware (stock movements, purchases)GET /wares/{uuid}

References without their own endpoint ​

Some references have no endpoint to load them back. In reports, that applies to resource and insuranceService. A target type without its own OAuth endpoint also stays a plain UUID with no way to fetch it: MedicalExternalLetter, MotherChildPass, Psychotherapy, Receipt, DVPReportEntry, and Transmission. For wares, groups stays an exception to the UUID form: a ware group has no UUID, so the field still returns [{ "name": "..." }].

Exceptions in the existing set ​

Embedded objects in appointments (customer, service, location, user), customers, and invoices stay unchanged for now — they are not part of this change. The exception is course in an appointment: it holds only the course UUID.

Access with the Tenancies add-on ​

If your company uses the Tenancies add-on, invoices, fee notes, cash revenues, and dunnings check the tenancy of every record. A record is reachable only if the owning user of your API access has access to its tenancy and holds the matching permission there:

EndpointPermission per tenancy
GET /invoices, GET /fee-notes, GET /cash-revenues and their single fetchesRechnungen: Anzeigen
GET /invoices/dunnings and single fetchMahnwesen: Anzeigen
POST /invoices/dunningsMahnwesen: Erstellen

This means:

  • A list contains only records from tenancies the owning user may see. meta.total counts only those records.
  • Records without a tenancy are not reachable.
  • A single fetch of a record outside those tenancies responds with 404 not_found, exactly like an unknown UUID.
  • POST /invoices/dunnings for an invoice of an inaccessible tenancy, or without the Mahnwesen: Erstellen permission in that tenancy, responds with 404 not_found (error_description: Invoice not found.) — like an unknown invoice.
  • GET /fee-note-services/turnover and GET /fee-note-services/productivity respond with 403 access_denied when no tenancy is accessible to the owning user. A report over only the records without a tenancy would otherwise look complete.

Without the Tenancies add-on nothing changes.

Slim responses with view ​

Seven list endpoints accept the query parameter view: GET /appointments, GET /customers, GET /executions, GET /invoices, GET /locations, GET /services, and GET /users. Allowed values are default (the default) and slim. With slim, the list returns only the most important fields per record. References to other records come as { "id": "<uuid>" }.

Any other value or an array returns 400 validation_error with Parameter "view" must be "default" or "slim". Single fetches do not read the parameter. If view is given more than once, the last value wins — ?view=default&view=slim returns the slim response.

EndpointFields with view=slim
GET /appointmentsid, title, start, end, allDay, status, type, source, deleted, deletedAt, createdAt, updatedAt. customer, service, location, and user as { "id" }, course as an object with id, name, maxAttendees, and currentAttendees
GET /customersid, title, firstName, lastName, companyName, sex, birthday, email, phone, street, zip, city, country, customerNumber, archived, deleted, createdAt, updatedAt, deletedAt
GET /executionsid, user, location, service (each as { "id" })
GET /invoicesid, sequence, formattedSequence, status, net, subTotal, gross, discount, outstandingBalance, invoicingDate, paidAt, isFullyPaid, deleted, createdAt, updatedAt. customer as { "id" }
GET /locationsid, name, abbreviation, street, zip, city, country, bookable, sort
GET /servicesid, name, duration, price, type, sort. category as { "id" }
GET /usersid, title, firstName, lastName, abbreviation, isDoctor, deleted, createdAt, updatedAt

Pagination ​

A paginated list uses the parameters offset (default: 0) and limit (default: 50, max: 200) and returns the total count in meta.total. Two endpoints return their results without pagination: GET /todos/{uuid}/references returns all of a todo's references without database pagination, and GET /customers/{uuid}/groups returns all of a customer's groups without offset/limit.

offset, limit, and maxDays (on Next Available Slots) must be integers. Any other value (abc, 5.5, 1e3, whitespace) returns 400 validation_error with Parameter "limit" must be an integer. (or the respective parameter name). Previously such a value was silently converted to a number. A value above or below the bounds is still clamped to the bound, and a missing parameter uses the default.

meta.total is counted in the database before the permission check applies. The tenancy check for invoices, fee notes, cash revenues, and dunnings is the exception: it applies within the query itself, see Access with the Tenancies add-on. What that means for pagination is covered in Incremental Sync.

Default sorting ​

Without a sort or timestamp filter, a list sorts by internal ID ascending. The internal ID is the record's database ID, not the id response field (that is the UUID). This order stays stable across the entire pagination run. That applies to every converted list, explicitly including dunning levels, customer groups, todos, courses, attendees, appointments, customers, users, fee notes, and invoices.

Three exceptions remain:

  • GET /wares sorts by relevance instead of internal ID when the term parameter is set.
  • Stock movements and purchases keep their previously documented sorting in filter mode.
  • GET /fee-note-services sorts by date descending, then by id descending.

Date and time parameters ​

The OAuth API parses the date and time query parameters strictly, and so it does start on Book Appointment. That includes from/until on the Reports, dueDateFrom/dueDateTo on Todos, startDate/endDate on available slots, from/until on Appointments, and start on Book Appointment.

Accepted formats:

  • a plain date YYYY-MM-DD (e.g. 2026-08-24) — only on endpoints that don't require a time
  • date and time with minute precision, no seconds (2026-08-24T10:00) — the seconds are then assumed to be 00
  • date and time with seconds (2026-08-24T10:00:00) or with milliseconds (2026-08-24T08:00:00.000Z)
  • each with a time zone offset (2026-08-24T10:00+02:00, 2026-08-24T10:00:00+02:00) or as UTC with Z (2026-08-24T10:00Z, 2026-08-24T08:00:00.000Z)

All fields are two digits (2026-08-04, not 2026-8-4), and the year is between 1000 and 9999. An offset takes the form Z, ±HH:MM, or ±HHMM and is at most ±14:00. The lowercase t and z are allowed. This applies to the query parameters and to start.

The following are rejected with 400 validation_error: zone names (Europe/Vienna, CEST, UTC), an offset without minutes (+02), an offset with seconds, single-digit month, day, or hour values, and a space before the offset.

All body fields that expect only a date (e.g. date, birthday, customDate, customStartDate) accept only YYYY-MM-DD as a real calendar date, with a year between 1000 and 9999. A timestamp, single-digit values, or an impossible date return 400 validation_error with Invalid <field>. Expected a date (YYYY-MM-DD).; null or an absent field is unchanged. Body fields with a time (start on Book Appointment, dueDate on Create Todo and Update Todo) accept the ISO formats listed above.

A time zone offset in the value is normalized internally to the server's time zone. An impossible calendar date such as 2026-02-31 returns 400 validation_error, not a silent rollover to a neighboring day. A time with a space instead of T (2026-08-24 10:00) or a relative format (tomorrow) are also rejected.

If the parameter is present but invalid, the endpoint responds with that parameter's format error — not with a message saying the parameter is missing or required, for example:

Parameter "from" must be an ISO 8601 date or date/time (e.g. "2026-08-24", "2026-08-24T10:00", "2026-08-24T10:00:00", "2026-08-24T10:00:00+02:00" or "2026-08-24T08:00:00.000Z").

A + in the time zone offset must be percent-encoded as %2B in the URL.

For a range made of two parameters, the start must not be after the end: createdSince after createdUntil, updatedSince after updatedUntil, and dueDateFrom after dueDateTo return 400 validation_error. For appointments, the same applies to from and until.

Interaction with timestamp filters ​

The deletedSince parameter returns only deleted or canceled records and combines with status using AND logic. Combining deletedSince with status=active contradicts itself — the response is an empty list, not an error. With status=deleted (or no status at all), deletedSince returns the deleted records from that point on, as expected.

On many endpoints, deletedSince also cannot be combined with createdSince, createdUntil, updatedSince, or updatedUntil — that is a separate rule, independent of status. Details on that, on building a cursor across pages, and on the pagination residual risk are covered in Incremental Sync.

Offisy GmbH