List Appointments
Returns a paginated list of all appointments of the company.
Breaking Change
The course of a course appointment (data[].course) is now a UUID instead of an embedded course object. Load course data and attendees through GET /courses/{uuid}.
Without the new status parameter, this list now also returns cancelled appointments. Get only active appointments with status=active.
Without a timestamp filter, the list now sorts by internal ID ascending instead of start time descending.
Request
GET /api/oauth/v2/resource/appointments
Header
| Header | Value |
|---|---|
Authorization | Bearer {access_token} |
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
offset | integer | No | Start position (default: 0) |
limit | integer | No | Number of results (default: 50, max: 200) |
from | string (ISO 8601) | Conditional | Range start — as date (2026-03-01) or ISO 8601 (2026-03-01T00:00:00+01:00). Required as long as neither updatedSince nor deletedSince is set — see the note below the table. |
until | string (ISO 8601) | Conditional | Range end — as date (2026-03-31) or ISO 8601 (2026-03-31T23:59:59+01:00). Required as long as neither updatedSince nor deletedSince is set — see the note below the table. |
type | string | No | Comma-separated list of appointment types (default: admin,customer,course). Allowed values: admin, customer, course, pause, private, vacation, illness, official_holiday, external. Special value all returns all types. |
customerId | string (UUID) | No | Filter appointments by a specific customer. Returns only appointments where the customer is directly assigned or appears in the attendee list of a course appointment. |
status | string | No | Comma-separated list (default: all). Allowed values: active, deleted. active returns only non-cancelled appointments. Invalid value → 400 validation_error. See Conventions for details. |
updatedSince | string (ISO 8601) | No | Only appointments modified at or after this timestamp (updatedAt >=). Newly created appointments are included, because the modification timestamp is set on creation. |
updatedUntil | string (ISO 8601) | No | Only appointments modified at or before this timestamp (updatedAt <=) |
deletedSince | string (ISO 8601) | No | Only cancelled appointments with a cancellation timestamp at or after this value (deletedAt >=). This filter does not return active appointments. |
view | string | No | default (the default) or slim. slim returns only the most important fields per appointment — see Conventions for details. Invalid value → 400 validation_error. |
Note: There are two ways to query appointments. With
fromanduntilyou fetch a date window — the API returns only appointments within that range. WithupdatedSince(ordeletedSince) you fetch changes instead, regardless of the appointment date.You always need one of the two. If
fromoruntilis missing, and neitherupdatedSincenordeletedSinceis set, the API responds with400 validation_error.
Important: Do not send a date window for an incremental sync.
from/untilandupdatedSinceotherwise combine — you only get changes inside that window and miss the rest. For a complete sync, leavefromanduntilout.
Tip: When provided without a time (e.g.
2026-03-01),fromis set to00:00:00anduntilto23:59:59— ideal for day-exact queries.
Note: This endpoint always requires both scopes
appointmentsandusers. For course appointments (typecourseorall), the scopecustomersis additionally required.
Note: If
customerIdis provided, the scopecustomersis additionally required — regardless of the chosen appointment type.
Note on course appointments: For course appointments (
type=course),courseholds only the course UUID. Load course data, attendees, and instructors throughGET /courses/{uuid}andGET /courses/{uuid}/attendees. The scopecustomersis still required for course appointments. If you explicitly request?type=course(or?type=all) without this scope, the API responds with403 insufficient_scope.
Note: For a sync,
updatedSincealone is enough — it covers new and modified appointments. Fetch cancelled appointments with a second request usingdeletedSince.
Note:
deletedSincecannot be combined withupdatedSinceorupdatedUntil. The API responds with400 validation_error, becausedeletedSinceswitches the result set to cancelled appointments. So query changes and cancellations in two separate requests.
Note: Formats, time zone behavior, sorting, cursor strategy, and the
meta.totaltrap work the same across all timestamp filters. See Incremental Sync.The deletion sync with
deletedSinceadds one more case of the residual risk. An appointment restored during the run keeps its old timestamp and falls below the new cursor.
Note on restored appointments: A cancelled appointment can be restored. The cancellation timestamp is cleared in the process. The appointment therefore disappears from the deletion sync and shows up in the change sync with
deleted=falseinstead.If you removed the appointment locally on the cancellation, this is how you get it back. Prerequisite: your client evaluates
deleted. So always run both syncs, not just the deletion sync.
Note on cancellations from legacy data:
deletedSinceonly finds appointments that carry a cancellation timestamp. Appointments cancelled before that are not reachable this way, not even with a very early date.
Note: Appointments don't support
createdSinceorcreatedUntil. On an appointment,createdAtreturns the booking time, not the record's creation time. UseupdatedSincefor incremental syncs over appointments instead.
Response
Success (200 OK)
Example: Single appointment (type: "customer")
{
"data": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"title": "Erstgespräch",
"start": "2026-03-15T10:00:00+01:00",
"end": "2026-03-15T11:00:00+01:00",
"allDay": false,
"status": "active",
"type": "customer",
"course": null,
"note": "Bitte nüchtern erscheinen",
"customer": {
"id": "f0e1d2c3-b4a5-6789-fedc-ba9876543210",
"firstName": "Anna",
"lastName": "Muster"
},
"service": {
"id": "c3d4e5f6-a7b8-9012-cdef-012345678901",
"name": "Erstgespräch"
},
"location": {
"id": "e5f6a7b8-c9d0-1234-efab-234567890123",
"name": "Studio 1",
"street": "Hauptstraße 1",
"zip": "1010",
"city": "Wien",
"country": "AT"
},
"user": {
"id": "d4e5f6a7-b8c9-0123-defa-123456789012",
"firstName": "Max",
"lastName": "Muster",
"abbreviation": "MM"
},
"source": "oauth",
"deleted": false,
"deletedAt": null,
"stornoReason": null,
"cancelableUntil": null,
"createdAt": "2026-03-01T09:00:00+01:00",
"updatedAt": "2026-03-01T09:00:00+01:00"
}
],
"meta": {
"total": 500,
"offset": 0,
"limit": 50
}
}Example: Course appointment (type: "course")
{
"data": [
{
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"title": "Yoga Anfänger",
"start": "2026-03-20T09:00:00+01:00",
"end": "2026-03-20T10:00:00+01:00",
"allDay": false,
"status": "active",
"type": "course",
"course": "c3d4e5f6-a7b8-9012-cdef-012345678901",
"note": null,
"customer": null,
"service": null,
"location": {
"id": "e5f6a7b8-c9d0-1234-efab-234567890123",
"name": "Studio 1",
"street": "Hauptstraße 1",
"zip": "1010",
"city": "Wien",
"country": "AT"
},
"user": {
"id": "d4e5f6a7-b8c9-0123-defa-123456789012",
"firstName": "Max",
"lastName": "Muster",
"abbreviation": "MM"
},
"source": "oauth",
"createdAt": "2026-03-01T09:00:00+01:00",
"updatedAt": "2026-03-01T09:00:00+01:00"
}
],
"meta": {
"total": 500,
"offset": 0,
"limit": 50
}
}Response fields
| Field | Type | Description |
|---|---|---|
data | Array | List of appointments |
data[].id | string (UUID) | Unique appointment ID |
data[].title | string | Title of the appointment |
data[].start | string (ISO 8601) | null | Start time |
data[].end | string (ISO 8601) | null | End time |
data[].allDay | boolean | All-day appointment |
data[].status | string | Status of the appointment (active or request) |
data[].type | string | Appointment type (admin, customer, course, pause, private, vacation, illness, official_holiday, external) |
data[].course | string (UUID) | null | Course reference — only populated when type: "course", otherwise null. Load course data and attendees through GET /courses/{uuid} |
data[].note | string | Internal note |
data[].customer | object | null | Linked customer |
data[].customer.id | string (UUID) | Customer ID |
data[].customer.firstName | string | First name |
data[].customer.lastName | string | Last name |
data[].service | object | null | Linked service |
data[].service.id | string (UUID) | Service ID |
data[].service.name | string | Name of the service |
data[].location | object | null | Execution location of the appointment |
data[].location.id | string (UUID) | Location ID |
data[].location.name | string | Location name |
data[].location.street | string | null | Street |
data[].location.zip | string | null | ZIP code |
data[].location.city | string | null | City |
data[].location.country | string | null | Country |
data[].user | object | null | Staff member assigned to the appointment |
data[].user.id | string (UUID) | Staff member ID |
data[].user.firstName | string | First name |
data[].user.lastName | string | Last name |
data[].user.abbreviation | string | null | Abbreviation |
data[].source | string | Source of the appointment |
data[].deleted | boolean | true if the appointment is cancelled, otherwise false |
data[].deletedAt | string (ISO 8601) | null | Time of cancellation, null if not cancelled |
data[].stornoReason | string | null | Cancellation reason, null if not cancelled or no reason given |
data[].cancelableUntil | string (ISO 8601) | null | Time until which the appointment can be cancelled online, null if no deadline is set |
data[].createdAt | string (ISO 8601) | null | Time the appointment was booked — not the record's creation time |
data[].updatedAt | string (ISO 8601) | null | Last modification time |
meta.total | integer | Total number of appointments |
meta.offset | integer | Current start position |
meta.limit | integer | Number of returned entries |
Note: The
customerandserviceobjects contain only a summary with the most important fields (see table above).
Note: Courses can also be retrieved on their own and paginated via
GET /courses, without going through appointments.
curl -X GET "https://my.offisy.at/api/oauth/v2/resource/appointments?offset=0&limit=50&from=beispiel&until=beispiel&type=admin%2Ccustomer%2Ccourse&customerId=b2c3d4e5-f6a7-8901-bcde-f01234567890&status=all&updatedSince=2026-04-15T10%3A00%3A00&updatedUntil=2026-04-15T10%3A00%3A00&deletedSince=beispiel&view=default" \
-H "Authorization: Bearer {access_token}"Possible errors
| Status | Code | Description |
|---|---|---|
| 400 | validation_error | from and/or until missing while neither updatedSince nor deletedSince is set — or the value is invalid |
| 400 | validation_error | from is after until (error_description: "from" must be before or equal to "until".) |
| 400 | validation_error | A timestamp filter contains an invalid value, or updatedSince is after updatedUntil |
| 400 | validation_error | deletedSince was combined with updatedSince or updatedUntil |
| 400 | validation_error | status contains an invalid value |
| 400 | validation_error | view is neither default nor slim |
| 401 | invalid_client | Invalid or missing access token |
| 403 | insufficient_scope | Scope appointments or users missing (both always required) |
| 403 | insufficient_scope | ?type=course/?type=all requested but scope customers missing |