Przejdź do treści
Sursa Developers

Synchronizacja przyrostowa z updated_since

Aby utrzymywać kopię danych Sursy we własnym systemie, nie musisz za każdym razem odczytywać wszystkiego. Listy obiektów, które mają updated_at, przyjmują updated_since i zwracają tylko obiekty zmienione w tym momencie lub później.

curl -G https://api.sursa.com/v1/cards \
  -H "Authorization: Bearer $SURSA_API_KEY" \
  --data-urlencode "updated_since=2026-10-23T00:00:00Z" \
  --data-urlencode "limit=200"

Z updated_since lista jest sortowana rosnąco według updated_at, a następnie według id, a kursor kontynuuje w tej kolejności. Dzięki temu odczytujesz zmiany w kolejności, w jakiej nastąpiły, a granica strony nigdy nie dzieli ich tak, że któraś zostanie utracona.

updated_since przyjmują te listy:

ListaCo liczy się jako zmiana
GET /v1/cards, GET /v1/cards/countupdated_at karty.
GET /v1/groupsupdated_at grupy: jej nazwa lub ustawienia.
GET /v1/webhooksupdated_at webhooka: jego ustawienia i sekret, nie jego dostarczenia.
GET /v1/jobsupdated_at zadania: każda zmiana jego statusu.
GET /v1/draftsupdated_at szkicu.
GET /v1/cards/{card_id}/threadsOstatnie utworzenie lub edycja komentarza głównego albo dowolnej odpowiedzi. Wątek przychodzi w całości.
GET /v1/folders, GET /v1/collections/{name}/foldersupdated_at folderu. Pasujące foldery przychodzą jako płaska lista, zob. niżej.

Kolekcje, użytkownicy i ogłoszenia nie mają updated_at, więc ich list nie można według niego filtrować: są krótkie, dlatego odczytuj je w całości przy każdym uruchomieniu. Czas musi zawierać przesunięcie strefy czasowej, np. Z lub +02:00; każda inna wartość jest odrzucana, tak jak w GET /v1/cards.

Z updated_since drzewo folderów przestaje być drzewem: folders każdej kolekcji zawiera zmienione foldery jako płaską listę, z pustym children, a parent_id wskazuje, gdzie każdy z nich się znajduje. GET /v1/folders pomija wtedy kolekcje bez zmienionych folderów.

Pętla synchronizacji

  1. Przy pierwszym uruchomieniu odczytaj całą listę bez updated_since i zapamiętaj moment, w którym uruchomienie się rozpoczęło.
  2. Przy każdym kolejnym uruchomieniu przekaż zapamiętany moment jako updated_since, pomniejszony o niewielki zapas, np. jedną minutę, i zapamiętaj nowy czas rozpoczęcia.
  3. Zapisz każdy obiekt w swoim magazynie danych według jego id, zastępując poprzednią kopię. Obiekty, które z powodu zapasu pojawią się dwukrotnie, zostaną po prostu zapisane ponownie.
  4. Każdą następną stronę pobieraj z kursorem i tym samym updated_since. Kursor działa tylko z filtrami pierwszej strony: bez nich żądanie zwraca 400 z kodem api.cursor_invalid.

Użycie czasu rozpoczęcia uruchomienia, a nie czasu jego zakończenia, gwarantuje, że zmiana wprowadzona w trakcie uruchomienia zostanie uwzględniona przy następnym.

GET /v1/cards zwraca tablicę JSON, więc jej następna strona przychodzi w nagłówku X-Next-Cursor (zob. Listy zwracające tablicę):

const BASE = "https://api.sursa.com";
const headers = { Authorization: `Bearer ${process.env.SURSA_API_KEY}` };

async function syncCards(since, saveCard) {
  const startedAt = new Date();
  const params = new URLSearchParams({ limit: "200" });
  if (since) params.set("updated_since", new Date(since.getTime() - 60_000).toISOString());
  let query = params.toString();
  while (query) {
    const response = await fetch(`${BASE}/v1/cards?${query}`, { headers });
    if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
    for (const card of await response.json()) await saveCard(card); // write into your store by card.id
    const cursor = response.headers.get("X-Next-Cursor");
    if (cursor) params.set("cursor", cursor); // keep updated_since: the cursor needs the same filters
    query = cursor ? params.toString() : "";
  }
  return startedAt; // pass it as `since` next time
}

Usunięcia

Lista zwraca tylko istniejące obiekty, więc nie może poinformować, że coś zostało usunięte. Usunięcia pobieraj z kanału zdarzeń:

curl -G https://api.sursa.com/v1/webhook-events \
  -H "Authorization: Bearer $SURSA_API_KEY" \
  --data-urlencode "type=card.deleted" \
  --data-urlencode "created_since=2026-10-23T00:00:00Z"

Do odczytu kanału potrzebny jest klucz z rolą admin i zakresem webhooks:manage. Zdarzenia są przechowywane przez 30 dni. Jeśli Twoja synchronizacja nie była uruchamiana dłużej, odczytaj ponownie pełną listę i usuń ze swojego magazynu danych każdy obiekt, którego już w niej nie ma.

Eksport do hurtowni danych łączy listy, updated_since i kanał zdarzeń w jedno zadanie.