Mostre um quadro e mantenha-o atualizado
Renderize um quadro do Dailybot Plan em uma requisição e mantenha-o em dia com o feed de mudanças: ETag e 304, consultas periódicas no ritmo do servidor, truncamento e expiração do cursor.
Beta
Plan está em beta. Tudo o que está em /plan no aplicativo web, os comandos da CLI e da agent skill para projetos, metas, quadros e tarefas, e a API pública /v1/plan/ podem mudar antes da disponibilidade geral. Quer testar com sua equipe? Escreva para [email protected].
Uma tela de quadro precisa de duas coisas: o quadro inteiro uma vez e, depois, apenas o que mudou. A API do Plan entrega exatamente isso: um snapshot que renderiza todas as colunas em uma requisição e um feed de mudanças que retorna as tarefas alteradas desde um cursor. Consulte o feed; nunca leia o quadro inteiro de novo em intervalos fixos.
1. Leia o quadro uma vez
curl -sS -D headers.txt "https://api.dailybot.com/v1/plan/boards/$BOARD/board/" \
-H "Authorization: Bearer $DAILYBOT_TOKEN" > board.json
A resposta tem uma entrada em groups por coluna, na ordem das colunas. Cada grupo traz o task_count real da coluna, suas primeiras tarefas por ordem de rank e has_more quando a coluna tem mais cartões do que cabem na página (até 50 por coluna, definido com tasks_per_state). Guarde duas coisas:
delta_cursordo corpo: onde o feed de mudanças começa.- O header
ETag: envie-o de volta comoIf-None-Matchpara receber304 Not Modifiedquando nada tiver mudado.
Para paginar o restante de uma coluna longa, use a lista de tarefas com o mesmo quadro e o mesmo estado: GET /v1/plan/tasks/?board=$BOARD&state=<state uuid>.
2. Peça apenas o que mudou
curl -sS "https://api.dailybot.com/v1/plan/boards/$BOARD/delta/?updated_since=$CURSOR" \
-H "Authorization: Bearer $DAILYBOT_TOKEN"
{
"since": "2026-09-25T10:14:02.113954Z",
"cursor": "2026-09-25T10:19:44.902311Z",
"changed": [ { "uuid": "00000000-0000-4000-8000-000000000005", "key": "ENG-142", "state": { "name": "Done", "category": "done" }, "version": 9 } ],
"removed": [ { "uuid": "00000000-0000-4000-8000-000000000105", "key": "ENG-77", "reason": "archived" } ],
"states": null,
"truncated": false,
"poll_after_seconds": 15
}
Aplique a resposta nesta ordem:
states: quando não fornull, uma coluna foi criada, renomeada, reordenada ou arquivada: substitua toda a sua lista de colunas por ela.changed: faça upsert de cada tarefa poruuid(uma entrada por tarefa; a versão atual prevalece).removed: retire cada tarefa do quadro.- Guarde o
cursore envie-o exatamente como veio emupdated_sincena próxima chamada. Nunca calcule um cursor com base no seu próprio relógio.
A entrega é at-least-once: uma tarefa alterada no mesmo instante do seu cursor pode chegar duas vezes, então os upserts precisam ser idempotentes.
3. Deixe o servidor definir o ritmo
- Espere
poll_after_secondsantes da próxima chamada. O valor começa em 15 segundos, dobra até 120 enquanto o quadro está parado e volta a cair assim que algo muda. - Se
truncatedfortrue, há mais mudanças esperando: consulte de novo imediatamente. - Pause enquanto a página estiver oculta e consulte uma vez quando ela voltar a ficar visível.
- Um cursor com mais de 7 dias retorna
400 delta_window_expired: leia o snapshot de novo e recomece a partir dodelta_cursordele. - O feed de mudanças permite 240 chamadas por minuto por ator. Seguir
poll_after_secondsmantém você bem abaixo desse limite.
Um loop de consultas completo
Um loop em shell para um script ou um dashboard no terminal:
CURSOR=$(jq -r .delta_cursor board.json)
while true; do
RESPONSE=$(curl -sS "https://api.dailybot.com/v1/plan/boards/$BOARD/delta/?updated_since=$CURSOR" \
-H "Authorization: Bearer $DAILYBOT_TOKEN")
if [ "$(echo "$RESPONSE" | jq -r '.code // empty')" = "delta_window_expired" ]; then
echo "Cursor expired: read the snapshot again"; break
fi
echo "$RESPONSE" | jq -c '{changed: [.changed[].key], removed: [.removed[].key]}'
CURSOR=$(echo "$RESPONSE" | jq -r .cursor)
if [ "$(echo "$RESPONSE" | jq -r .truncated)" = "true" ]; then continue; fi
sleep "$(echo "$RESPONSE" | jq -r .poll_after_seconds)"
done
O mesmo loop em um serviço Node.js (Node 18 ou posterior), com o token guardado no servidor:
const API = 'https://api.dailybot.com/v1/plan';
const headers = { Authorization: `Bearer ${process.env.DAILYBOT_TOKEN}` };
const sleep = (s) => new Promise((resolve) => setTimeout(resolve, s * 1000));
async function follow(boardId, onChange) {
let res = await fetch(`${API}/boards/${boardId}/board/`, { headers });
let cursor = (await res.json()).delta_cursor;
for (;;) {
res = await fetch(`${API}/boards/${boardId}/delta/?updated_since=${encodeURIComponent(cursor)}`, { headers });
const delta = await res.json();
if (delta.code === 'delta_window_expired') return follow(boardId, onChange);
onChange(delta); // replace columns if delta.states, upsert delta.changed, drop delta.removed
cursor = delta.cursor;
if (!delta.truncated) await sleep(delta.poll_after_seconds);
}
}
No navegador, chame o seu próprio backend em vez de colocar um token do Dailybot no código da página, e pause o loop no visibilitychange enquanto a aba estiver oculta.
Referência
GET /v1/plan/boards/{board_id}/board/: o snapshotGET /v1/plan/boards/{board_id}/delta/: o feed de mudanças- Convenções do Plan: ETag e
304