Salt la conținut
Sursa Developers

Sincronizare incrementală cu updated_since

Pentru a păstra o copie a datelor din Sursa în propriul sistem, nu trebuie să citiți totul de fiecare dată. Listele obiectelor care au updated_at acceptă updated_since și returnează doar obiectele modificate în acel moment sau după el.

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"

Cu updated_since, lista este sortată crescător după updated_at, apoi după id, iar cursorul continuă în aceeași ordine. Astfel citiți modificările în ordinea în care s-au produs, iar granița dintre pagini nu le desparte niciodată în așa fel încât una să se piardă.

updated_since este acceptat de aceste liste:

ListăCe se consideră o modificare
GET /v1/cards, GET /v1/cards/countupdated_at al fișei.
GET /v1/groupsupdated_at al grupului: titlul sau setările lui.
GET /v1/webhooksupdated_at al webhookului: setările și secretul lui, nu livrările lui.
GET /v1/jobsupdated_at al jobului: fiecare schimbare a stării lui.
GET /v1/draftsupdated_at al schiței.
GET /v1/cards/{card_id}/threadsCea mai recentă creare sau modificare a comentariului rădăcină ori a unui răspuns. Firul vine complet.
GET /v1/folders, GET /v1/collections/{name}/foldersupdated_at al dosarului. Dosarele potrivite vin ca listă simplă, vedeți mai jos.

Colecțiile, utilizatorii și anunțurile nu au updated_at, așa că listele lor nu filtrează după el: sunt scurte, deci citiți-le integral la fiecare rulare. Momentul trebuie să aibă decalajul fusului orar, de exemplu Z sau +02:00; orice altă valoare este refuzată, ca la GET /v1/cards.

Cu updated_since, un arbore de dosare nu mai este arbore: folders din fiecare colecție conține dosarele modificate ca listă simplă, cu children goale, iar parent_id arată unde se află fiecare. GET /v1/folders omite în acest caz colecțiile fără dosare modificate.

O buclă de sincronizare

  1. La prima rulare, citiți întreaga listă fără updated_since și rețineți momentul în care rularea a început.
  2. La fiecare rulare următoare, transmiteți momentul reținut ca updated_since, minus o mică marjă, de exemplu un minut, și rețineți noul moment de început.
  3. Scrieți fiecare obiect în spațiul de stocare după id-ul lui, înlocuind copia anterioară. Obiectele care apar de două ori din cauza marjei sunt pur și simplu scrise din nou.
  4. Cereți fiecare pagină următoare cu cursorul și același updated_since. Un cursor funcționează doar cu filtrele primei pagini: fără ele, cererea returnează 400 cu codul api.cursor_invalid.

Folosind momentul de început al rulării, nu pe cel de sfârșit, vă asigurați că o modificare făcută în timpul rulării este preluată de rularea următoare.

GET /v1/cards returnează un tablou JSON, așa că pagina următoare vine în antetul X-Next-Cursor (vedeți Liste care returnează un tablou):

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
}

Ștergeri

O listă returnează doar obiectele care există, așa că nu vă poate spune că ceva a fost șters. Preluați ștergerile din fluxul de evenimente:

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"

Citirea fluxului necesită o cheie cu rolul admin și domeniul webhooks:manage. Evenimentele se păstrează 30 de zile. Dacă sincronizarea nu a rulat mai mult de atât, citiți din nou lista completă și eliminați din spațiul de stocare fiecare obiect care nu mai este în ea.

Exportul într-un depozit de date reunește listele, updated_since și fluxul de evenimente într-un singur job.