Skip to content

List Appointments ​

Returns a paginated list of all appointments of the company.

Request ​

GET /api/oauth/v2/resource/appointments

HeaderValue
AuthorizationBearer {access_token}

Query parameters ​

ParameterTypeRequiredDescription
offsetintegerNoStart position (default: 0)
limitintegerNoNumber of results (default: 50, max: 200)
fromstring (ISO 8601)ConditionalRange 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.
untilstring (ISO 8601)ConditionalRange 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.
typestringNoComma-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.
customerIdstring (UUID)NoFilter appointments by a specific customer. Returns only appointments where the customer is directly assigned or appears in the attendee list of a course appointment.
viewstringNoResponse shape: default (default) or slim
updatedSincestring (ISO 8601)NoOnly appointments modified at or after this timestamp (updatedAt >=). Newly created appointments are included, because the modification timestamp is set on creation.
updatedUntilstring (ISO 8601)NoOnly appointments modified at or before this timestamp (updatedAt <=)
deletedSincestring (ISO 8601)NoOnly 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 from and until you fetch a date window — the API returns only appointments within that range. With updatedSince (or deletedSince) you fetch changes instead, regardless of the appointment date.

You always need one of the two. If from or until is missing, and neither updatedSince nor deletedSince is set, the API responds with 400 validation_error.

Important: Do not send a date window for an incremental sync. from/until and updatedSince otherwise combine — you only get changes inside that window and miss the rest. For a complete sync, leave from and until out.

Tip: When provided without a time (e.g. 2026-03-01), from is set to 00:00:00 and until to 23:59:59 — ideal for day-exact queries.

Note: This endpoint always requires both scopes appointments and users. For course appointments (type course or all), the scope customers is additionally required.

Note: If customerId is provided, the scope customers is 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 scope customers is required for this. If you explicitly request ?type=course (or ?type=all) without this scope, the API responds with 403 insufficient_scope.

Note: For a sync, updatedSince alone is enough — it covers new and modified appointments. Fetch cancelled appointments with a second request using deletedSince.

Note: deletedSince cannot be combined with updatedSince or updatedUntil. The API responds with 400 validation_error, because deletedSince switches 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.total trap work the same across all timestamp filters. See Incremental Sync.

The deletion sync with deletedSince adds 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 = false instead.

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: deletedSince only 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 createdSince or createdUntil. On an appointment, createdAt returns the booking time, not the record's creation time. Use updatedSince for incremental syncs over appointments instead.

Response ​

Success (200 OK) ​

Example: Single appointment (type: "customer")

json
{
  "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")

json
{
  "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 ​

FieldTypeDescription
dataArrayList of appointments
data[].idstring (UUID)Unique appointment ID
data[].titlestringTitle of the appointment
data[].startstring (ISO 8601) | nullStart time
data[].endstring (ISO 8601) | nullEnd time
data[].allDaybooleanAll-day appointment
data[].statusstringStatus of the appointment (active or request)
data[].typestringAppointment type (admin, customer, course, pause, private, vacation, illness, official_holiday, external)
data[].courseobject | nullCourse data — only populated when type: "course", otherwise null
data[].course.idstring (UUID)Course ID
data[].course.namestringCourse name
data[].course.maxAttendeesinteger | nullMaximum number of attendees
data[].course.currentAttendeesintegerCurrent number of attendees
data[].course.colorstring | nullColor as hex code (e.g. #ff0000)
data[].course.instructorsArrayList of instructors
data[].course.instructors[].idstring (UUID)Instructor ID
data[].course.instructors[].firstNamestringFirst name of the instructor
data[].course.instructors[].lastNamestringLast name of the instructor
data[].course.locationobject | nullCourse location
data[].course.location.idstring (UUID)Location ID
data[].course.location.namestringLocation name
data[].course.attendeesArrayList of course attendees
data[].course.attendees[].customerobjectAttendee summary
data[].course.attendees[].customer.idstring (UUID)Customer ID
data[].course.attendees[].customer.firstNamestringFirst name
data[].course.attendees[].customer.lastNamestringLast name
data[].course.attendees[].bookingDatestring (ISO 8601) | nullBooking date
data[].notestringInternal note
data[].customerobject | nullLinked customer
data[].customer.idstring (UUID)Customer ID
data[].customer.firstNamestringFirst name
data[].customer.lastNamestringLast name
data[].serviceobject | nullLinked service
data[].service.idstring (UUID)Service ID
data[].service.namestringName of the service
data[].locationobject | nullExecution location of the appointment
data[].location.idstring (UUID)Location ID
data[].location.namestringLocation name
data[].location.streetstring | nullStreet
data[].location.zipstring | nullZIP code
data[].location.citystring | nullCity
data[].location.countrystring | nullCountry
data[].userobject | nullStaff member assigned to the appointment
data[].user.idstring (UUID)Staff member ID
data[].user.firstNamestringFirst name
data[].user.lastNamestringLast name
data[].user.abbreviationstring | nullAbbreviation
data[].sourcestringSource of the appointment
data[].deletedbooleantrue if the appointment is cancelled, otherwise false
data[].deletedAtstring (ISO 8601) | nullTime of cancellation, null if not cancelled
data[].stornoReasonstring | nullCancellation reason, null if not cancelled or no reason given
data[].createdAtstring (ISO 8601) | nullTime the appointment was booked — not the record's creation time
data[].updatedAtstring (ISO 8601) | nullLast modification time
meta.totalintegerTotal number of appointments
meta.offsetintegerCurrent start position
meta.limitintegerNumber of returned entries

Note: The customer and service objects 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: note and stornoReason are not included in the slim response.

Note: In the slim response, course includes neither attendees nor instructors. If you need the attendee list, request without view. This also makes the slim response the more data-sparing option — less data is transferred per appointment record.

Example: Course appointment, view=slim

json
{
  "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
  }
}
Beispiele
bash
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}"
Parameter
Zuerst Token anfordern

Possible errors ​

StatusCodeDescription
400validation_errorfrom and/or until missing while neither updatedSince nor deletedSince is set — or the value is invalid
400validation_errorInvalid value for view (valid values: default, slim)
400validation_errorA timestamp filter contains an invalid value
400validation_errordeletedSince was combined with updatedSince or updatedUntil
401invalid_clientInvalid or missing access token
403insufficient_scopeScope appointments or users missing (both always required)
403insufficient_scope?type=course/?type=all requested but scope customers missing

Offisy GmbH