Skip to content

Termine abrufen ​

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

Breaking Change

Der Kurs eines Kurs-Termins (data[].course) ist jetzt eine UUID statt eines eingebetteten Kurs-Objekts. Kursdaten und Teilnehmer lädst du über GET /courses/{uuid} nach.

Ohne den neuen Parameter status liefert diese Liste jetzt auch stornierte Termine. Nur aktive Termine bekommst du mit status=active.

Ohne Zeitstempel-Filter sortiert die Liste jetzt nach interner ID aufsteigend statt nach Startzeit absteigend.

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.
statusstringNeinKommaseparierte Liste (Standard: all). Erlaubte Werte: active, deleted. active liefert nur nicht stornierte Termine. Ungültiger Wert → 400 validation_error. Details siehe Konventionen.
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.
viewstringNeindefault (Standard) oder slim. slim liefert pro Termin nur die wichtigsten Felder – Details siehe Konventionen. Ungültiger Wert → 400 validation_error.

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: Bei Kurs-Terminen (type=course) steht in course nur die UUID des Kurses. Kursdaten, Teilnehmer und Kursleiter lädst du über GET /courses/{uuid} und GET /courses/{uuid}/attendees nach. Für Kurs-Termine ist trotzdem 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,
      "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
  }
}

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": "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-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[].coursestring (UUID) | nullKurs-Referenz – nur bei type: "course" befüllt, sonst null. Kursdaten und Teilnehmer lädst du über GET /courses/{uuid} nach
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[].cancelableUntilstring (ISO 8601) | nullZeitpunkt, bis zu dem der Termin online storniert werden kann, null wenn keine Frist gesetzt ist
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).

Hinweis: Kurse sind auch eigenständig über GET /courses abrufbar und paginierbar, ohne über Termine zu gehen.

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&status=all&updatedSince=2026-04-15T10%3A00%3A00&updatedUntil=2026-04-15T10%3A00%3A00&deletedSince=beispiel&view=default" \
  -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_errorfrom liegt nach until (error_description: "from" must be before or equal to "until".)
400validation_errorEin Zeitstempel-Filter enthält einen ungültigen Wert, oder updatedSince liegt nach updatedUntil
400validation_errordeletedSince wurde mit updatedSince oder updatedUntil kombiniert
400validation_errorstatus enthält einen ungültigen Wert
400validation_errorview ist weder default noch slim
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