List Appointments
Returns a paginated list of all appointments of the company.
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. |
view | string | No | Response shape: default (default) or slim |
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. |
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: Course appointments (
type=course) contain personal customer data (attendees.customer) and user data (instructors). The scopecustomersis required for this. 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,
"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": {
"id": "c3d4e5f6-a7b8-9012-cdef-012345678901",
"name": "Yoga Anfänger",
"maxAttendees": 20,
"currentAttendees": 12,
"color": "#ff0000",
"instructors": [
{ "id": "d4e5f6a7-b8c9-0123-defa-123456789012", "firstName": "Max", "lastName": "Muster" }
],
"location": { "id": "e5f6a7b8-c9d0-1234-efab-234567890123", "name": "Studio 1" },
"attendees": [
{
"customer": { "id": "f0e1d2c3-b4a5-6789-fedc-ba9876543210", "firstName": "Anna", "lastName": "Muster" },
"bookingDate": "2026-03-01T10:00:00+01:00"
}
]
},
"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 | object | null | Course data — only populated when type: "course", otherwise null |
data[].course.id | string (UUID) | Course ID |
data[].course.name | string | Course name |
data[].course.maxAttendees | integer | null | Maximum number of attendees |
data[].course.currentAttendees | integer | Current number of attendees |
data[].course.color | string | null | Color as hex code (e.g. #ff0000) |
data[].course.instructors | Array | List of instructors |
data[].course.instructors[].id | string (UUID) | Instructor ID |
data[].course.instructors[].firstName | string | First name of the instructor |
data[].course.instructors[].lastName | string | Last name of the instructor |
data[].course.location | object | null | Course location |
data[].course.location.id | string (UUID) | Location ID |
data[].course.location.name | string | Location name |
data[].course.attendees | Array | List of course attendees |
data[].course.attendees[].customer | object | Attendee summary |
data[].course.attendees[].customer.id | string (UUID) | Customer ID |
data[].course.attendees[].customer.firstName | string | First name |
data[].course.attendees[].customer.lastName | string | Last name |
data[].course.attendees[].bookingDate | string (ISO 8601) | null | Booking date |
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[].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).
Slim response (?view=slim)
The view=slim query parameter returns a slim response with only the most important fields. Without the parameter, the response stays unchanged — existing integrations don't need to change anything.
If view is given more than once, the last value wins — ?view=default&view=slim returns the slim response.
The slim response includes the following fields:
id, title, start, end, allDay, status, type, source, deleted, deletedAt, createdAt, updatedAt, customer (id only), service (id only), location (id only), user (id only), course (id, name, maxAttendees, currentAttendees)
Note:
noteandstornoReasonare not included in the slim response.Note: In the slim response,
courseincludes neitherattendeesnorinstructors. If you need the attendee list, request withoutview. This also makes the slim response the more data-sparing option — less data is transferred per appointment record.
Example: Course appointment, view=slim
{
"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",
"source": "oauth",
"deleted": false,
"deletedAt": null,
"createdAt": "2026-03-01T09:00:00+01:00",
"updatedAt": "2026-03-01T09:00:00+01:00",
"customer": null,
"service": null,
"location": { "id": "e5f6a7b8-c9d0-1234-efab-234567890123" },
"user": { "id": "d4e5f6a7-b8c9-0123-defa-123456789012" },
"course": {
"id": "c3d4e5f6-a7b8-9012-cdef-012345678901",
"name": "Yoga Anfänger",
"maxAttendees": 20,
"currentAttendees": 12
}
}
],
"meta": {
"total": 500,
"offset": 0,
"limit": 50
}
}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&view=default&updatedSince=2026-04-15T10%3A00%3A00&updatedUntil=2026-04-15T10%3A00%3A00&deletedSince=beispiel" \
-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 | Invalid value for view (valid values: default, slim) |
| 400 | validation_error | A timestamp filter contains an invalid value |
| 400 | validation_error | deletedSince was combined with updatedSince or updatedUntil |
| 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 |