Інкрементна синхронізація з 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/count | updated_at картки. |
GET /v1/groups | updated_at групи: її назва або налаштування. |
GET /v1/webhooks | updated_at вебхука: його налаштування та секрет, а не його доставки. |
GET /v1/jobs | updated_at завдання: кожна зміна його статусу. |
GET /v1/drafts | updated_at чернетки. |
GET /v1/cards/{card_id}/threads | Останнє створення чи редагування кореневого коментаря або будь-якої відповіді. Гілка приходить повністю. |
GET /v1/folders, GET /v1/collections/{name}/folders | updated_at папки. Папки, що відповідають умові, приходять пласким списком, див. нижче. |
Колекції, користувачі й оголошення не мають updated_at, тож їхні списки за ним не фільтруються: вони короткі, тому
читайте їх повністю під час кожного запуску. Час має містити зсув часового поясу, як-от Z або +02:00; будь-яке
інше значення відхиляється, як і в GET /v1/cards.
З updated_since дерево папок уже не є деревом: folders кожної колекції містить змінені папки пласким списком, з
порожнім children, а parent_id показує, де розташована кожна з них.
GET /v1/folders тоді пропускає колекції без змінених папок.
Цикл синхронізації
- Під час першого запуску прочитайте весь список без
updated_sinceі запам’ятайте момент, коли запуск почався. - Під час кожного наступного запуску передавайте запам’ятований момент як
updated_since, мінус невеликий запас, наприклад хвилину, і запам’ятовуйте новий час початку. - Записуйте кожен об’єкт у своє сховище за його
id, замінюючи попередню копію. Об’єкти, що через запас трапляються двічі, просто записуються ще раз. - Запитуйте кожну наступну сторінку з курсором і тим самим
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 і стрічку подій в
одному завданні.