Preskoči na sadržaj
Sursa Developers

Inkrementalna sinkronizacija pomoću updated_since

Da biste u vlastitom sustavu čuvali kopiju podataka iz Surse, ne morate svaki put čitati sve. Popisi objekata koji imaju updated_at prihvaćaju updated_since i vraćaju samo objekte promijenjene u tom trenutku ili nakon njega.

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"

Uz updated_since popis je poredan uzlazno po updated_at, zatim po id, a kursor nastavlja tim redoslijedom. Tako promjene čitate redoslijedom kojim su se dogodile, a granica stranice nikada ih ne razdvaja tako da se neka izgubi.

updated_since prihvaćaju ovi popisi:

PopisŠto se smatra promjenom
GET /v1/cards, GET /v1/cards/countupdated_at kartice.
GET /v1/groupsupdated_at grupe: njezin naslov ili postavke.
GET /v1/webhooksupdated_at webhooka: njegove postavke i tajna, ne njegove isporuke.
GET /v1/jobsupdated_at posla: svaka promjena njegova statusa.
GET /v1/draftsupdated_at nacrta.
GET /v1/cards/{card_id}/threadsPosljednje stvaranje ili uređivanje osnovnog komentara ili bilo kojeg odgovora. Nit stiže cijela.
GET /v1/folders, GET /v1/collections/{name}/foldersupdated_at mape. Odgovarajuće mape stižu kao ravan popis, v. u nastavku.

Zbirke, korisnici i obavijesti nemaju updated_at, pa njihovi popisi ne filtriraju prema njemu: kratki su, stoga ih pri svakom pokretanju pročitajte cijele. Vrijeme mora sadržavati pomak vremenske zone, kao što je Z ili +02:00; svaka druga vrijednost odbija se, kao kod GET /v1/cards.

Uz updated_since stablo mapa više nije stablo: folders svake zbirke sadrži promijenjene mape kao ravan popis, s praznim children, a parent_id pokazuje gdje se svaka od njih nalazi. GET /v1/folders tada izostavlja zbirke bez promijenjenih mapa.

Petlja sinkronizacije

  1. Pri prvom pokretanju pročitajte cijeli popis bez updated_since i zapamtite trenutak u kojem je pokretanje započelo.
  2. Pri svakom sljedećem pokretanju proslijedite taj zapamćeni trenutak kao updated_since, umanjen za malu rezervu, na primjer jednu minutu, i zapamtite novo vrijeme početka.
  3. Svaki objekt zapišite u svoju pohranu prema njegovu id, zamjenjujući prethodnu kopiju. Objekti koji se zbog rezerve pojave dvaput jednostavno se ponovno zapisuju.
  4. Svaku sljedeću stranicu zatražite s kursorom i istim updated_since. Kursor radi samo s filtrima prve stranice: bez njih zahtjev vraća 400 s kodom api.cursor_invalid.

Ako koristite vrijeme početka pokretanja, a ne vrijeme njegova završetka, promjenu nastalu dok je pokretanje trajalo sigurno će preuzeti sljedeće pokretanje.

GET /v1/cards vraća JSON niz, pa njegova sljedeća stranica stiže u zaglavlju X-Next-Cursor (v. Popisi koji vraćaju niz):

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
}

Brisanja

Popis vraća samo objekte koji postoje, pa vam ne može reći da je nešto izbrisano. Brisanja preuzimajte iz feeda događaja:

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"

Za čitanje feeda potreban je ključ s ulogom admin i opsegom webhooks:manage. Događaji se čuvaju 30 dana. Ako se vaša sinkronizacija nije pokretala dulje od toga, ponovno pročitajte cijeli popis i iz svoje pohrane uklonite svaki objekt kojeg u njemu više nema.

Izvoz u skladište podataka objedinjuje popise, updated_since i feed događaja u jedan posao.