# Mantenha seus dados atualizados

Um painel só vale sua URL se os números nele estiverem atuais. Essa é a parte que uma sessão de agente não pode deixar para trás: seu agente constrói o painel e vai embora, e os dados por baixo precisam continuar chegando em um agendamento, por meses, sem que ninguém precise acompanhar.

A atualidade dos dados é, portanto, estado da plataforma, não código do painel. Você a configura uma vez contra um conjunto de dados; ela continua rodando quer alguém esteja com uma sessão aberta, quer não.

## As três formas de os dados avançarem[​](#as-três-formas-de-os-dados-avançarem "Link direto para As três formas de os dados avançarem")

|                             | O que é                                               | Quem dispara                                                           |
| --------------------------- | ----------------------------------------------------- | ---------------------------------------------------------------------- |
| **Atualização sob demanda** | Rebuscar um conjunto de dados agora                   | Você, seu agente, ou seu CI — `POST /v1/datasets/{dataset_id}/refresh` |
| **Carga incremental**       | Buscar apenas o que mudou desde a última execução     | A mesma chamada, com `mode: "incremental"`                             |
| **Atualização agendada**    | Uma expressão cron que a plataforma executa para você | Ninguém — o poller de cron da plataforma                               |

## Atualize um conjunto de dados agora[​](#refresh-a-dataset-now "Link direto para Atualize um conjunto de dados agora")

```
result = client.datasets.refresh(dataset_id)

print(result.completed, result.effective_mode, result.message)
```

```
const result = await client.datasets.refresh(datasetId);

console.log(result.completed, result.effectiveMode, result.message);
```

Dois resultados possíveis, e a diferença importa:

* **200 OK** — o trabalho realmente terminou dentro da requisição. `completed` é `true` e `dataset` é a linha final. É isso que um novo download de um conjunto de dados público faz.
* **202 Accepted** — a atualização foi *iniciada*. A linha é travada e marcada como `PROCESSING`, e um conector roda segundos depois. `completed` é `false`. Faça polling do conjunto de dados, ou escute no websocket, até que `processingStatus` saia de `PROCESSING`.

Um `202` não é uma falha e não é um sucesso — é uma promessa. Trate "a chamada retornou" e "os dados estão atuais" como dois fatos diferentes, e verifique o segundo antes de dizer a alguém que o painel está em dia. A chamada que verifica isso é [`get_data_freshness`](#how-old-are-the-numbers) — tudo o que vem antes dessa seção trata de fazer os dados se moverem, não de quão antigos eles são.

## Cargas incrementais, e a execução que ainda não é incremental[​](#cargas-incrementais-e-a-execução-que-ainda-não-é-incremental "Link direto para Cargas incrementais, e a execução que ainda não é incremental")

Passe um modo para buscar apenas linhas novas:

```
from vibedasher.models import RefreshRequest



result = client.datasets.refresh(dataset_id, RefreshRequest(mode="incremental"))

print(result.requested_mode, "->", result.effective_mode)
```

`requestedMode` ecoa o que você pediu. **`effectiveMode` é o que de fato rodou, e eles nem sempre são iguais.** Uma requisição incremental contra um conjunto de dados que ainda não tem checkpoint retorna `effectiveMode: "bootstrap"`: ela roda como uma sobrescrita completa *e* registra a marca d'água, então a **próxima** execução é a primeira genuinamente incremental.

Baseie sua lógica em `effectiveMode`, nunca no que você pediu. (Essa é a mesma regra de `client.query`, que reporta a via de execução que de fato usou, em vez daquela que você pediu — um padrão que vale a pena internalizar: a plataforma diz o que aconteceu, e é seu trabalho ler isso.)

Para forçar a próxima execução a buscar tudo de novo, limpe a marca d'água:

```
client.datasets.reset_checkpoint(dataset_id)
```

## Atualização agendada[​](#atualização-agendada "Link direto para Atualização agendada")

Um agendamento é uma linha que você cria uma vez. Ela carrega uma expressão cron e o projeto dbt que atualiza:

```
from vibedasher.models import RefreshCreate



schedule = client.refreshes.create_refresh(

    RefreshCreate(name="nightly", project_id=project_id, cron="0 3 * * *")

)

print(schedule.refresh.next_run, schedule.schedule_registration.registered)
```

A resposta carrega um bloco `scheduleRegistration`. **Verifique `registered`.** Ele é `true` somente quando o agendamento está de fato ativo; quando é `false`, `reason` e `message` dizem o porquê, e `nextRun` é nulo.

Esse campo existe por causa de uma interrupção real, e a história é o melhor argumento a favor do design atual. A implementação original registrava cada agendamento como sua própria regra na nuvem, envolvia a chamada em um `except` amplo, e respondia `201 Created` com um `nextRun` confiante. O papel da API não tinha permissão para criar essas regras, então **nada jamais foi registrado** — e todo cliente era informado de que seus dados seriam atualizados. Isso foi descoberto em 05-09-2026: zero regras existiam, e o único agendamento no banco de dados tinha um `nextRun` cinco meses no passado e uma contagem de execuções igual a zero.

A correção não foi um tratamento de erros melhor. Foi remover a coisa que podia falhar: **a linha *é* o agendamento.** Um poller de cron roda a cada cinco minutos, reivindica cada linha vencida com um compare-and-set, e a executa. Não há registro externo para ter sucesso ou falhar, então a única coisa que pode dar errado no momento da criação é uma expressão cron que não pode ser avaliada — e isso agora é rejeitado logo de início, não no momento do disparo.

Duas consequências que vale a pena planejar:

* Um agendamento dispara **dentro de cerca de cinco minutos** do seu horário previsto, não no segundo exato. Cron aqui significa "não antes de", não "exatamente às".
* `scheduleRegistration.backend` traz `db_cron`. Ele é mantido na resposta porque clientes baseiam lógica nele.

## Lendo o que aconteceu[​](#lendo-o-que-aconteceu "Link direto para Lendo o que aconteceu")

Cada linha de agendamento carrega seu próprio histórico — `lastRun`, `lastStatus`, `lastError`, `executionCount`, `nextRun`:

```
for r in client.refreshes.iter_all():

    print(r.name, r.last_status, r.last_run, r.execution_count, r.last_error)
```

`executionCount` é o campo a observar. Um agendamento que existe há uma semana com `executionCount: 0` não está lento — ele não está rodando, e é exatamente essa a falha que o design acima foi construído para tornar visível em vez de silenciosa.

## Qual é a idade destes números?[​](#how-old-are-the-numbers "Link direto para Qual é a idade destes números?")

Tudo acima responde a *«o pipeline está funcionando?»*. Não responde a *«o que este painel mostra está atual?»*, e as duas coisas se separam nas duas direções:

* Um agendamento pode rodar perfeitamente e não carregar nada de novo. `executionCount` sobe, `lastStatus` é `success`, e a linha mais recente ainda é de março.
* **Um conjunto de dados pode não ter agendamento algum**, e então `executionCount` não tem linha para ler. Um upload estático é o caso comum: suas linhas mudam quando alguém sobe novas e em nenhum outro momento, então uma lista de agendamentos saudável pode conviver com um conjunto de dados de nove dias sem dizer nada sobre ele. A ausência de um agendamento não é um agendamento com contador ruim.

Uma única chamada responde à pergunta real do leitor, medida a partir das linhas e não da contabilidade da própria plataforma:

```
f = client.datasets.get_freshness(dataset_id).freshness

print(f.as_of, f.age_days, f.status)
```

```
const { freshness } = await client.datasets.getFreshness(datasetId);

console.log(freshness.asOf, freshness.ageDays, freshness.status);
```

No MCP é `get_data_freshness`, que aceita **exatamente um** de `viz_id` ou `dataset_id`. Com um `viz_id`, ele resolve cada conjunto de dados que o painel lê e reporta **o mais antigo** — um painel só está tão atual quanto sua entrada mais velha. A rota REST é `GET /v1/datasets/{dataset_id}/freshness`.

### Os três campos, e por que são três[​](#os-três-campos-e-por-que-são-três "Link direto para Os três campos, e por que são três")

| Campo          | O que é                                                                                                                                                                                                      |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `asOf`         | O valor mais recente na **própria coluna temporal dos dados**. Esta é a resposta, e ela não pode lisonjear: é lida das linhas. `ageDays` é a idade disso; negativo significa dados com data futura.          |
| `lastLoadedAt` | Quando as linhas **chegaram** pela última vez. Um fato de pipeline, não uma idade — um arquivo carregado hoje de manhã pode não conter nada mais recente que março. Nunca cite isso como data de referência. |
| `refresh`      | Se há algo agendado para deixar os números mais novos, **incluindo a resposta «nada, e nada pode haver»** para um arquivo plano. É o campo que cobre o caso «sem agendamento» descrito acima.                |

`status` diz quanto confiar em `asOf`: `MEASURED` (é real), `NO_TIME_COLUMN` (os dados não têm coluna de data, portanto não têm data de referência a ter), ou `UNKNOWN` — que significa que o fato não foi estabelecido e **nunca** é uma aprovação.

`lastLoadedSource` nomeia qual relógio respondeu a `lastLoadedAt`. `iceberg_snapshot` é o horário físico de escrita do snapshot atual da tabela — o único instante de chegada verdadeiro, e um que uma renomeação não pode mover. **`not_established` significa que `lastLoadedAt` é `null` de propósito**: aquele caminho de leitura não consultou o snapshot, então em vez de responder com o `updatedAt` da linha — um relógio de escrita que qualquer edição adianta, e que uma vez reportou linhas chegando nove dias depois do que chegaram — o campo recusa. Um campo que recusa é estritamente melhor que um que responde com o relógio errado. A rota acima é a chamada que lê o certo. `get_viz` e `list_vizzes` carregam um `dataFreshness` gratuito, definido sobre a postura de atualização e `UNKNOWN` sobre a data de referência; esta chamada é o que resolve esse `UNKNOWN`.

### O que *não* é a idade dos dados[​](#o-que-não-é-a-idade-dos-dados "Link direto para o-que-não-é-a-idade-dos-dados")

Nem `publishStatus`, nem `lastBuild.createdAt`, nem `processingStatus`, nem `updatedAt` respondem a esta pergunta. Eles são, em ordem: se um bundle existe, quando o **código** foi compilado, se o arquivo foi analisado uma vez, e quando a **linha** mudou pela última vez. Cada um deles se lê como *atual* em um painel cujos números têm semanas. Se você está a ponto de reportar atualidade a partir de qualquer um deles, chame a rota de frescor em vez disso.

Custa um `SELECT MAX(<coluna de data>)` por conjunto de dados — a mesma sondagem que o painel renderizado já executa ao montar.

## Onde vive cada gesto[​](#onde-vive-cada-gesto "Link direto para Onde vive cada gesto")

* **As programações são a superfície MCP.** `create_refresh`, `list_refreshes`, `get_refresh` e `delete_refresh` cobrem todo o ciclo de vida de uma atualização recorrente a partir do seu agente.
* **"Atualize agora" é o SDK e a API REST.** Uma execução avulsa é uma única chamada lá — veja a **[Referência da API](/pt-BR/reference/vibedasher-sdk-api.md)**.
* **Projetos dbt** carregam suas próprias rotas de execução e atualização (`/v1/dbt/projects/{project_id}/run`, `/v1/dbt/projects/{project_id}/refreshes`).
