Pular para o conteúdo
Sursa Developers

Sincronização incremental com updated_since

Para manter uma cópia dos dados do Sursa no seu próprio sistema, você não precisa ler tudo a cada vez. As listas de objetos que têm updated_at aceitam updated_since e retornam só os objetos alterados naquele momento ou depois dele.

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"

Com updated_since, a lista é ordenada por updated_at em ordem crescente e depois por id, e o cursor continua nessa ordem. Assim, você lê as alterações na ordem em que aconteceram, e o limite entre páginas nunca as separa de um jeito que faça uma delas se perder.

updated_since é aceito por estas listas:

ListaO que conta como alteração
GET /v1/cards, GET /v1/cards/countupdated_at do cartão.
GET /v1/groupsupdated_at do grupo: o título ou as configurações dele.
GET /v1/webhooksupdated_at do webhook: as configurações e o segredo dele, não as entregas.
GET /v1/jobsupdated_at do job: cada mudança de status dele.
GET /v1/draftsupdated_at do rascunho.
GET /v1/cards/{card_id}/threadsA criação ou a edição mais recente do comentário raiz ou de qualquer resposta. A thread vem inteira.
GET /v1/folders, GET /v1/collections/{name}/foldersupdated_at da pasta. As pastas que atendem ao filtro vêm como uma lista plana; veja abaixo.

As coleções, os usuários e os comunicados não têm updated_at, então as listas deles não filtram por ele: como são curtas, leia-as inteiras a cada execução. O horário precisa ter um deslocamento de fuso horário, como Z ou +02:00; qualquer outro valor é recusado, como em GET /v1/cards.

Com updated_since, uma árvore de pastas deixa de ser uma árvore: folders de cada coleção traz as pastas alteradas como uma lista plana, com children vazio, e parent_id indica onde cada uma fica. GET /v1/folders então omite as coleções sem pastas alteradas.

Um loop de sincronização

  1. Na primeira execução, leia a lista inteira sem updated_since e guarde o momento em que a execução começou.
  2. A cada execução seguinte, passe esse momento guardado como updated_since, menos uma pequena margem, como um minuto, e guarde o novo horário de início.
  3. Grave cada objeto no seu armazenamento pelo id, substituindo a cópia anterior. Os objetos que aparecem duas vezes por causa da margem simplesmente são gravados de novo.
  4. Peça cada página seguinte com o cursor e o mesmo updated_since. Um cursor só funciona com os filtros da primeira página: sem eles, a requisição retorna 400 com o código api.cursor_invalid.

Usar o horário de início da execução, e não o horário em que ela terminou, garante que uma alteração feita durante a execução seja captada pela próxima.

GET /v1/cards retorna um array JSON, então a próxima página dele vem no cabeçalho X-Next-Cursor (veja Listas que retornam um 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
}

Exclusões

Uma lista só retorna objetos que existem, então ela não consegue avisar que algo foi excluído. Obtenha as exclusões no feed de eventos:

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"

Para ler o feed, é preciso uma chave com a função admin e o escopo webhooks:manage. Os eventos são mantidos por 30 dias. Se a sua sincronização ficou sem rodar por mais tempo que isso, leia a lista inteira de novo e remova do seu armazenamento todo objeto que não estiver mais nela.

Exportar para um data warehouse junta as listas, updated_since e o feed de eventos em um único job.