Инкрементальная синхронизация с 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 и ленту событий в одно задание.