Към съдържанието
Sursa Developers

Инкрементална синхронизация с updated_since

За да поддържате копие на данните от Sursa в собствената си система, не е нужно всеки път да четете всичко. Списъците на обекти, които имат updated_at, приемат updated_since и връщат само обектите, променени в този момент или след него.

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"

С updated_since списъкът е подреден по updated_at във възходящ ред, после по id, и курсорът продължава в същия ред. Така четете промените в реда, в който са станали, и границата между страниците никога не ги разделя така, че някоя да се изгуби.

updated_since се приема от тези списъци:

СписъкКакво се смята за промяна
GET /v1/cards, GET /v1/cards/countupdated_at на картата.
GET /v1/groupsupdated_at на групата: заглавието или настройките ѝ.
GET /v1/webhooksupdated_at на уебхука: настройките и тайната му, но не и доставките му.
GET /v1/jobsupdated_at на заданието: всяка промяна на статуса му.
GET /v1/draftsupdated_at на черновата.
GET /v1/cards/{card_id}/threadsПоследното създаване или редактиране на основния коментар или на някой от отговорите. Нишката идва цялата.
GET /v1/folders, GET /v1/collections/{name}/foldersupdated_at на папката. Съвпадащите папки идват като плосък списък, вж. по-долу.

Колекциите, потребителите и съобщенията нямат updated_at, затова списъците им не се филтрират по него: те са кратки, така че ги четете целите при всяко изпълнение. Времето трябва да съдържа отместване на часовата зона, например Z или +02:00; всяка друга стойност се отказва, както при GET /v1/cards.

С updated_since дървото на папките вече не е дърво: folders на всяка колекция съдържа променените папки като плосък списък с празен children, а parent_id показва къде се намира всяка от тях. GET /v1/folders тогава пропуска колекциите без променени папки.

Цикъл на синхронизация

  1. При първото изпълнение прочетете целия списък без updated_since и запомнете момента, в който изпълнението е започнало.
  2. При всяко следващо изпълнение подайте запомнения момент като updated_since, минус малък резерв, например една минута, и запомнете новото начално време.
  3. Записвайте всеки обект в хранилището си по неговото id, като заменяте предишното копие. Обектите, които се появяват два пъти заради резерва, просто се записват отново.
  4. Изисквайте всяка следваща страница с курсора и със същия updated_since. Курсорът работи само с филтрите на първата страница: без тях заявката връща 400 с кода api.cursor_invalid.

Когато използвате началното време на изпълнението, а не времето на завършването му, промяна, направена докато изпълнението тече, със сигурност ще бъде взета при следващото.

GET /v1/cards връща JSON масив, затова следващата му страница идва в заглавката X-Next-Cursor (вж. Списъци, които връщат масив):

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
}

Изтривания

Списъкът връща само обекти, които съществуват, затова не може да ви каже, че нещо е изтрито. Вземайте изтриванията от лентата със събития:

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"

За четенето на лентата е нужен ключ с ролята admin и обхвата webhooks:manage. Събитията се пазят 30 дни. Ако синхронизацията ви не е стартирала по-дълго от това, прочетете отново пълния списък и премахнете от хранилището си всеки обект, който вече не е в него.

Експорт в хранилище за данни събира списъците, updated_since и лентата със събития в едно задание.