Skip to content

Inkrementeller Abgleich ​

Sieben Endpoints unterstützen Zeitstempel-Filter für einen inkrementellen Abgleich: createdSince, createdUntil, updatedSince, updatedUntil und deletedSince. Welche Filter an welchem Endpoint verfügbar sind, steht in der Parameter-Tabelle des jeweiligen Endpoints.

Erlaubte Zeitformate ​

Erlaubt sind ISO 8601 mit Minuten- oder Sekundenpräzision, jeweils mit oder ohne Zeitzonen-Offset bzw. als UTC mit Z (2026-08-01T00:00+02:00, 2026-08-01T00:00:00, 2026-08-01T00:00:00Z), sowie ein reines Datum (2026-08-01). Bruchteilsekunden sind ebenfalls erlaubt (2026-08-24T10:00:00.123+02:00).

Das ist genau das Format, das new Date().toISOString() in JavaScript erzeugt. Gängige Rust- und Python-Serializer liefern dasselbe Format.

Bei einem reinen Datum setzt die API *Since-Parameter auf 00:00:00 und *Until-Parameter auf 23:59:59. Unterstützt ein Endpoint einen Parameter nicht, ignoriert die API ihn still.

Ungültige Werte ​

Ein ungültiger Wert wird mit 400 validation_error abgelehnt und nicht korrigiert. 2026-02-30 verschiebt die API also nicht still auf den 2. März.

Liegt createdSince nach createdUntil oder updatedSince nach updatedUntil, antwortet die API ebenfalls mit 400 validation_error. Gleiche Werte sind erlaubt.

Zeitzonen ​

Ein Wert ohne UTC-Offset gilt als lokale Zeit des Servers. Weicht deine Zeitzone davon ab, verschiebt sich das Abfragefenster um die Differenz. An der Sync-Grenze fehlen dir dann Änderungen, oder du bekommst sie doppelt.

Schicke den Wert deshalb immer mit Offset, zum Beispiel 2026-08-01T00:00:00+02:00. Die API rechnet den Wert intern auf die Server-Zeitzone um. Auch die zurückgegebenen Zeitstempel liegen in der Server-Zeitzone.

Sortierung im Filter-Modus ​

Ohne Zeitstempel-Filter sortieren Listen nach interner ID aufsteigend (siehe Konventionen). Sobald du einen Zeitstempel-Filter setzt, bleibt diese Sortierung unverändert. Sie bleibt über die gesamte Paginierung stabil.

Ein Datensatz kann dir dadurch höchstens doppelt geliefert werden, aber nie fehlen. Pflege deine Daten deshalb per Upsert über die id ein – dann sind Doppelte unkritisch.

Cursor für den nächsten Durchlauf ​

Die API sortiert im Filter-Modus nach interner ID, nicht nach Zeitstempel. Das letzte Element einer Seite trägt deshalb nicht den jüngsten Zeitstempel. Bildest du deinen Cursor daraus, überspringst du Änderungen.

Nimm stattdessen den größten Zeitstempel über alle Seiten des gesamten Durchlaufs. Setze ihn erst, wenn der Durchlauf vollständig abgeschlossen ist.

Bricht der Durchlauf ab, lass den alten Cursor stehen und wiederhole den Durchlauf komplett. Zieh vom neuen Cursor außerdem eine kleine Sicherheitsmarge ab, etwa eine Minute.

Restrisiko der Paginierung ​

Ein Datensatz kann trotzdem übersprungen werden, wenn er die Ergebnismenge während der Paginierung verlässt. Die Menge schrumpft dann, und alles dahinter rutscht eine Position vor.

Der Datensatz an der Seitengrenze fällt so aus dem Durchlauf. Sein Zeitstempel bleibt unverändert und liegt unter dem Cursor, den du danach setzt – er wird also nicht nachgeholt.

Das passiert typischerweise, wenn ein Datensatz während des Durchlaufs über eine gesetzte updatedUntil-Grenze hinaus bearbeitet wird. Lass updatedUntil beim inkrementellen Abgleich deshalb weg.

meta.total bei eingeschränkten Rechten ​

total wird in der Datenbank gezählt, die Berechtigungsprüfung greift erst danach. Hat dein API-Zugang eingeschränkte Rechte, enthalten die Seiten weniger Datensätze als total erwarten lässt.

Eine Abbruchbedingung wie „paginieren, bis alle total Datensätze angekommen sind" terminiert deshalb nicht. Die Berechtigungsprüfung entfernt Zeilen erst aus der bereits per limit geschnittenen Seite. Eine kurze oder leere Seite bedeutet deshalb nicht automatisch das Ende der Daten.

Paginiere stattdessen über offset:

  • Erhöhe offset bei jedem Request um limit.
  • Wiederhole das, solange offset < total.
  • Eine Seite kann dabei weniger Datensätze liefern als limit – sogar leer sein, ohne dass die Daten zu Ende sind.
  • Brich ab, sobald offset >= total.

Wann der Vollabgleich trotzdem nötig ist ​

Der inkrementelle Abgleich ersetzt den Vollabgleich nicht in jedem Fall. Drei Situationen bleiben:

  • Rechnungen haben keinen eigenen Löschzeitpunkt, aber das Löschen setzt updatedAt. Eine Löschung ist deshalb inkrementell über updatedSince erkennbar – der Datensatz kommt mit deleted: true zurück. Honorarnoten unterstützen nur createdSince/createdUntil, und das Löschen ihrer Rechnung ändert die Honorarnote selbst nicht. Löschungen bleiben hier inkrementell unsichtbar, ein periodischer Vollabgleich über status=deleted bleibt nötig.
  • Ein wiederhergestellter Datensatz verschwindet aus dem Lösch-Abgleich. Er taucht stattdessen im Änderungs-Abgleich wieder auf, sofern dein Client das Feld deleted auswertet.
  • Ist meta.total größer als die Menge, die deine Rechte dir liefern, bleiben dir Datensätze verborgen. Das erkennst du nur mit einem Vollabgleich ohne Zeitstempel-Filter.

Details zu Löschzeitpunkten und Wiederherstellung stehen im jeweiligen Endpoint-Artikel.

Offisy GmbH