Vai al contenuto
Sursa Developers

Sincronizzazione incrementale con updated_since

Per mantenere una copia dei dati di Sursa nel tuo sistema non devi leggere tutto ogni volta. Gli elenchi degli oggetti che hanno updated_at accettano updated_since e restituiscono solo gli oggetti modificati in quel momento o dopo.

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"

Con updated_since, l’elenco è ordinato per updated_at crescente, poi per id, e il cursore prosegue in quell’ordine. Così leggi le modifiche nell’ordine in cui sono avvenute, e il confine tra due pagine non le divide mai in modo da perderne una.

updated_since è accettato da questi elenchi:

ElencoCosa conta come modifica
GET /v1/cards, GET /v1/cards/countupdated_at della scheda.
GET /v1/groupsupdated_at del gruppo: il suo titolo o le sue impostazioni.
GET /v1/webhooksupdated_at del webhook: le sue impostazioni e il suo segreto, non le sue consegne.
GET /v1/jobsupdated_at del job: ogni cambio del suo stato.
GET /v1/draftsupdated_at della bozza.
GET /v1/cards/{card_id}/threadsLa creazione o la modifica più recente del commento principale o di una qualsiasi risposta. La discussione arriva per intero.
GET /v1/folders, GET /v1/collections/{name}/foldersupdated_at della cartella. Le cartelle corrispondenti arrivano come elenco piatto, vedi sotto.

Raccolte, utenti e comunicazioni non hanno updated_at, quindi i loro elenchi non filtrano in base a esso: sono brevi, perciò leggili per intero a ogni esecuzione. L’orario deve avere uno scostamento dal fuso orario, come Z o +02:00; qualsiasi altro valore viene rifiutato, come con GET /v1/cards.

Con updated_since, un albero di cartelle non è più un albero: folders di ogni raccolta contiene le cartelle modificate come elenco piatto, con children vuoto, e parent_id indica dove si trova ciascuna. GET /v1/folders in questo caso omette le raccolte senza cartelle modificate.

Un ciclo di sincronizzazione

  1. Alla prima esecuzione, leggi l’intero elenco senza updated_since e annota il momento in cui l’esecuzione è iniziata.
  2. A ogni esecuzione successiva, passa quel momento come updated_since, meno un piccolo margine, per esempio un minuto, e annota il nuovo orario di inizio.
  3. Scrivi ogni oggetto nel tuo archivio in base al suo id, sostituendo la copia precedente. Gli oggetti che compaiono due volte a causa del margine vengono semplicemente scritti di nuovo.
  4. Richiedi ogni pagina successiva con il cursore e lo stesso updated_since. Un cursore funziona solo con i filtri della prima pagina: senza di essi la richiesta restituisce 400 con il codice api.cursor_invalid.

Usare l’orario di inizio dell’esecuzione, e non quello di fine, garantisce che una modifica fatta mentre l’esecuzione era in corso venga raccolta dalla successiva.

GET /v1/cards restituisce un array JSON, quindi la sua pagina successiva arriva nell’intestazione X-Next-Cursor (vedi Elenchi che restituiscono un array):

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
}

Eliminazioni

Un elenco restituisce solo gli oggetti che esistono, quindi non può dirti che qualcosa è stato eliminato. Ricava le eliminazioni dal feed degli eventi:

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"

Per leggere il feed serve una chiave con il ruolo admin e l’ambito webhooks:manage. Gli eventi vengono conservati per 30 giorni. Se la tua sincronizzazione non è stata eseguita per più tempo, leggi di nuovo l’elenco completo e rimuovi dal tuo archivio ogni oggetto che non vi compare più.

Esportare in un data warehouse riunisce elenchi, updated_since e feed degli eventi in un unico job.