Skip to content

Termine abrufen ​

Gibt eine paginierte Liste aller Termine des Unternehmens zurück.

Request ​

GET /api/oauth/v2/resource/appointments

HeaderWert
AuthorizationBearer {access_token}

Query-Parameter ​

ParameterTypPflichtBeschreibung
offsetintegerNeinStartposition (Standard: 0)
limitintegerNeinAnzahl Ergebnisse (Standard: 50, Max: 200)
fromstring (ISO 8601)BedingtZeitraum-Start – als Datum (2026-03-01) oder ISO 8601 (2026-03-01T00:00:00+01:00). Pflicht, solange weder updatedSince noch deletedSince gesetzt ist – siehe Hinweis unter der Tabelle.
untilstring (ISO 8601)BedingtZeitraum-Ende – als Datum (2026-03-31) oder ISO 8601 (2026-03-31T23:59:59+01:00). Pflicht, solange weder updatedSince noch deletedSince gesetzt ist – siehe Hinweis unter der Tabelle.
typestringNeinKommaseparierte Liste von Termintypen (Standard: admin,customer,course). Erlaubte Werte: admin, customer, course, pause, private, vacation, illness, official_holiday, external. Sonderwert all gibt alle Typen zurück.
customerIdstring (UUID)NeinFiltert Termine auf einen bestimmten Kunden. Gibt nur Termine zurück, bei denen der Kunde direkt zugewiesen ist oder in der Attendee-Liste eines Kurs-Termins vorkommt.
viewstringNeinAntwortform: default (Vorgabe) oder slim
updatedSincestring (ISO 8601)NeinNur Termine, die ab diesem Zeitpunkt geändert wurden (updatedAt >=). Neu angelegte Termine kommen mit, weil der Änderungszeitpunkt beim Anlegen gesetzt wird.
updatedUntilstring (ISO 8601)NeinNur Termine, die bis zu diesem Zeitpunkt geändert wurden (updatedAt <=)
deletedSincestring (ISO 8601)NeinNur stornierte Termine mit Löschzeitpunkt ab diesem Wert (deletedAt >=). Aktive Termine liefert dieser Filter nicht.

Hinweis: Du fragst Termine auf zwei Wegen ab. Mit from und until holst du ein Terminfenster – die API gibt nur Termine in diesem Zeitraum zurück. Mit updatedSince (oder deletedSince) holst du stattdessen Änderungen unabhängig vom Termindatum.

Eines von beiden brauchst du immer. Fehlt from oder until, und ist weder updatedSince noch deletedSince gesetzt, antwortet die API mit 400 validation_error.

Wichtig: Schicke beim inkrementellen Abgleich kein Terminfenster mit. from/until und updatedSince wirken sonst zusammen – du bekommst nur Änderungen innerhalb dieses Zeitfensters und verpasst den Rest. Für einen vollständigen Abgleich lässt du from und until weg.

Tipp: Bei Angabe ohne Uhrzeit (z.B. 2026-03-01) wird from auf 00:00:00 und until auf 23:59:59 gesetzt – ideal für tagesgenaue Abfragen.

Hinweis: Dieser Endpoint erfordert immer die Scopes appointments und users. Für Kurs-Termine (Typ course oder all) ist zusätzlich der Scope customers erforderlich.

Hinweis: Wird customerId angegeben, ist zusätzlich der Scope customers erforderlich – unabhängig vom gewählten Termintyp.

Hinweis zu Kurs-Terminen: Kurs-Termine (type=course) enthalten personenbezogene Kundendaten (attendees.customer) und Mitarbeiterdaten (instructors). Dafür ist der Scope customers erforderlich. Fragst du explizit ?type=course (oder ?type=all) an, ohne diesen Scope zu besitzen, antwortet die API mit 403 insufficient_scope.

Hinweis: Für einen Abgleich genügt updatedSince – der Wert deckt neue und geänderte Termine ab. Stornierte Termine holst du in einem zweiten Request mit deletedSince.

Hinweis: deletedSince lässt sich nicht mit updatedSince oder updatedUntil kombinieren. Die API antwortet dann mit 400 validation_error, weil deletedSince die Ergebnismenge auf stornierte Termine umschaltet. Frage Änderungen und Stornierungen deshalb in zwei getrennten Requests ab.

Hinweis: Formate, Zeitzonen-Verhalten, Sortierung im Filter-Modus, Cursor-Strategie und die meta.total-Falle gelten für alle Zeitstempel-Filter gleich – siehe Inkrementeller Abgleich.

Beim Lösch-Abgleich mit deletedSince kommt ein weiterer Fall des Restrisikos hinzu. Ein während des Durchlaufs wiederhergestellter Termin behält seinen alten Zeitstempel und rutscht unter den neuen Cursor.

Hinweis zu wiederhergestellten Terminen: Ein stornierter Termin kann wiederhergestellt werden. Dabei fällt der Löschzeitpunkt weg. Der Termin verschwindet damit aus dem Lösch-Abgleich und erscheint stattdessen im Änderungs-Abgleich mit deleted = false.

Hast du den Termin lokal auf die Storno-Meldung hin entfernt, holst du ihn dir so zurück. Voraussetzung: dein Client wertet deleted aus. Fahre deshalb immer beide Abgleiche, nicht nur den Lösch-Abgleich.

Hinweis zu Stornierungen aus dem Altbestand: deletedSince findet nur Termine mit gesetztem Löschzeitpunkt. Termine, die davor storniert wurden, erreichst du damit nicht – auch nicht mit einem sehr frühen Datum.

Hinweis: Termine unterstützen kein createdSince und kein createdUntil. Das Feld createdAt liefert am Termin den Buchungszeitpunkt, nicht den Erstellungszeitpunkt des Datensatzes. Für den inkrementellen Abgleich über Termine verwendest du deshalb updatedSince.

Response ​

Erfolg (200 OK) ​

Beispiel: Einzeltermin (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
  }
}

Beispiel: Kurs-Termin (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-Felder ​

FeldTypBeschreibung
dataArrayListe der Termine
data[].idstring (UUID)Eindeutige Termin-ID
data[].titlestringTitel des Termins
data[].startstring (ISO 8601) | nullStartzeit
data[].endstring (ISO 8601) | nullEndzeit
data[].allDaybooleanGanztägiger Termin
data[].statusstringStatus des Termins (active oder request)
data[].typestringTermintyp (admin, customer, course, pause, private, vacation, illness, official_holiday, external)
data[].courseobject | nullKurs-Daten – nur bei type: "course" befüllt, sonst null
data[].course.idstring (UUID)Kurs-ID
data[].course.namestringKursname
data[].course.maxAttendeesinteger | nullMaximale Teilnehmeranzahl
data[].course.currentAttendeesintegerAktuelle Teilnehmeranzahl
data[].course.colorstring | nullFarbe als Hex-Code (z.B. #ff0000)
data[].course.instructorsArrayListe der Kursleiter
data[].course.instructors[].idstring (UUID)Kursleiter-ID
data[].course.instructors[].firstNamestringVorname des Kursleiters
data[].course.instructors[].lastNamestringNachname des Kursleiters
data[].course.locationobject | nullKursort
data[].course.location.idstring (UUID)Ort-ID
data[].course.location.namestringOrtsname
data[].course.attendeesArrayListe der Kursteilnehmer
data[].course.attendees[].customerobjectTeilnehmer-Kurzinfo
data[].course.attendees[].customer.idstring (UUID)Kunden-ID
data[].course.attendees[].customer.firstNamestringVorname
data[].course.attendees[].customer.lastNamestringNachname
data[].course.attendees[].bookingDatestring (ISO 8601) | nullBuchungsdatum
data[].notestringInterne Notiz
data[].customerobject | nullVerknüpfter Kunde
data[].customer.idstring (UUID)Kunden-ID
data[].customer.firstNamestringVorname
data[].customer.lastNamestringNachname
data[].serviceobject | nullVerknüpfte Leistung
data[].service.idstring (UUID)Leistungs-ID
data[].service.namestringName der Leistung
data[].locationobject | nullAusführungsort des Termins
data[].location.idstring (UUID)Standort-ID
data[].location.namestringStandortname
data[].location.streetstring | nullStraße
data[].location.zipstring | nullPLZ
data[].location.citystring | nullOrt
data[].location.countrystring | nullLand
data[].userobject | nullDem Termin zugeordnete:r Mitarbeiter:in
data[].user.idstring (UUID)Mitarbeiter-ID
data[].user.firstNamestringVorname
data[].user.lastNamestringNachname
data[].user.abbreviationstring | nullKürzel
data[].sourcestringQuelle des Termins
data[].deletedbooleantrue wenn der Termin storniert ist, sonst false
data[].deletedAtstring (ISO 8601) | nullZeitpunkt der Stornierung, null wenn nicht storniert
data[].stornoReasonstring | nullStorno-Grund, null wenn nicht storniert oder kein Grund angegeben
data[].createdAtstring (ISO 8601) | nullBuchungszeitpunkt des Termins – nicht der Erstellungszeitpunkt des Datensatzes
data[].updatedAtstring (ISO 8601) | nullLetzter Änderungszeitpunkt
meta.totalintegerGesamtanzahl der Termine
meta.offsetintegerAktuelle Startposition
meta.limitintegerAnzahl der zurückgegebenen Einträge

Hinweis: Die Objekte customer und service enthalten nur eine Kurzform mit den wichtigsten Feldern (siehe Tabelle oben).

Schlanker Abruf (?view=slim) ​

Der Query-Parameter view=slim liefert eine schlanke Payload mit nur den wichtigsten Feldern. Ohne den Parameter bleibt der Abruf unverändert – bestehende Integrationen müssen nichts anpassen.

Wird view mehrfach angegeben, gilt die letzte Angabe – ?view=default&view=slim liefert also die schlanke Antwort.

Die schlanke Payload enthält folgende Felder:

id, title, start, end, allDay, status, type, source, deleted, deletedAt, createdAt, updatedAt, customer (nur id), service (nur id), location (nur id), user (nur id), course (id, name, maxAttendees, currentAttendees)

Hinweis: note und stornoReason sind im schlanken Abruf nicht enthalten.

Hinweis: course enthält im schlanken Abruf weder attendees noch instructors. Wer die Teilnehmerliste braucht, ruft ohne view ab. Das macht den schlanken Abruf gleichzeitig zur datensparsameren Variante – pro Termin-Datensatz werden weniger Daten übertragen.

Beispiel: Kurs-Termin, 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

Mögliche Fehler ​

StatusCodeBeschreibung
400validation_errorfrom und/oder until fehlt, obwohl weder updatedSince noch deletedSince gesetzt ist – oder der Wert ist ungültig
400validation_errorUngültiger Wert für view (gültige Werte: default, slim)
400validation_errorEin Zeitstempel-Filter enthält einen ungültigen Wert
400validation_errordeletedSince wurde mit updatedSince oder updatedUntil kombiniert
401invalid_clientUngültiger oder fehlender Access Token
403insufficient_scopeScope appointments oder users fehlt (beide immer erforderlich)
403insufficient_scope?type=course/?type=all angefragt, aber Scope customers fehlt

Offisy GmbH