Der Sync-Takt ist eine Eigenschaft der vorgelagerten Pipeline, nicht etwas, das die API konfiguriert oder offenlegt — sie hat keinen eigenen, maschinell erzwungenen Zeitplan, gegen den man das prüfen könnte. Gehe nicht von einem festen Aktualisierungsintervall aus — prüfe stattdessen immer
last_sync_at (siehe unten).TRUNCATE + Reload-Semantik
Bei jedem Sync-Lauf wird jede Tabelle vollständig gelöscht und komplett neu geladen — es gibt kein inkrementelles Update auf Zeilenebene innerhalb der Tabelle. Das hat zwei direkte Konsequenzen für Integratoren:- Kein partieller Sync. Eine Tabelle wird entweder vollständig durch den neuesten Sync ersetzt, oder gar nicht berührt. Es gibt keinen Mechanismus, um “nur die Zeilen zu synchronisieren, die sich geändert haben” innerhalb einer einzelnen Tabelle —
GET /v1/changes(unten) sagt dir, welche Tabellen sich geändert haben, nicht welche Zeilen. - Schema-Änderungen sind für kumulative Tabellen immer ein Breaking Change. Bei Tabellen, die über mehrere Reloads hinweg Historie ansammeln (zum Beispiel
contract_ends), kann eine Schema-Änderung nicht additiv angewendet werden — TRUNCATE + Reload ersetzt immer den gesamten Tabelleninhalt, es gibt also keine Möglichkeit, alte Zeilen in-place in eine neue Form zu migrieren. Jede Schema-Änderung an einer solchen Tabelle wird als Breaking Change behandelt, der eine neue API-Version erfordert, wobei sowohl die alte als auch die neue Tabellenform während eines Deprecation-Fensters parallel synchronisiert werden.
Wann last_sync_at gesetzt wird
last_sync_at wird nur gesetzt, wenn ein Sync für diese Tabelle erfolgreich abgeschlossen wird — es bleibt NULL, bis der erste erfolgreiche Sync stattfindet, und ein fehlgeschlagener oder noch laufender Sync treibt es nicht voran. Das bedeutet:
- Eine Tabelle ohne bisherigen erfolgreichen Sync erscheint niemals in
GET /v1/changes, unabhängig davon, wie kürzlich ein Sync versucht wurde. last_sync_atspiegelt den Abschluss auf Tabellenebene wider, nicht den Änderungszeitpunkt einer einzelnen Zeile innerhalb dieser Tabelle.
Was GET /v1/changes tatsächlich prüft
GET /v1/changes?since=<timestamp> liefert eine Tabelle nur zurück, wenn sie einen erfolgreichen Sync mit last_sync_at > since abgeschlossen hat — ein strikter Größer-als-Vergleich, kein >=.
Der ETag-Response-Header wird aus dem Maximum von last_sync_at über alle synchronisierten Tabellen eines Kunden sowie aus since (nach UTC umgerechnet) und dem Filter category gebildet. Ein ETag aus einer Abfrage mit anderem since oder anderer category passt daher nie. Polling mit If-None-Match erlaubt dir daher, einen Request komplett zu überspringen, wenn seit deinem letzten Poll nichts synchronisiert wurde (siehe Delta Sync für das vollständige Polling-Muster).
Praktische Auswirkungen für deine Integration
- Poll
/v1/changes, statt einen Zeitplan zu raten. Da es kein dokumentiertes, erzwungenes Sync-Intervall gibt, baue deine Integration so, dass sie auf das reagiert, was/v1/changesmeldet, statt anzunehmen, dass Daten zu einer festen Tageszeit aktualisiert werden. - Eine geänderte Tabelle bedeutet “alles für diese Tabelle neu abrufen”, nicht “hier sind die Diffs”. Änderungserkennung auf Zeilenebene existiert noch nicht — wenn eine Tabelle in
/v1/changeserscheint, ist der sicherste Weg ein vollständiger erneuter Abruf dieser Category oder dieses Raw-Endpunkts. - Behandle
last_sync_atals Aktualitätssignal, nicht als Änderungsprotokoll. Es sagt dir, dass die Tabelle zu diesem Zeitpunkt erfolgreich neu geladen wurde; es sagt dir nicht, welche Zeilen sich von der vorherigen Version unterscheiden.