---
title: Schedules
description: Cadences and intervals, anchors and timezones, the precision you actually get, and what happens across a daylight-saving change.
updated: 2026-08-04
---

A schedule answers one question: when does the scheduler re-run this dashboard's
SQL? Everything else on this page is a consequence of how that question is stored
and how the scheduler sweeps.

## Cadences and intervals

A schedule is a **time unit** plus a **multiplier**, not a free-form expression.
There is no cron string.

| Cadence | Multiplier allowed | Anchors it takes |
|---|---|---|
| `manual` | none | none |
| `hourly` | every 1, 2, 3, 4, 6, 8, or 12 hours | an optional hour |
| `daily` | every 1 to 30 days | an optional hour, with a timezone |
| `weekly` | every 1 to 4 weeks | a day of week, an hour and a timezone, all three or none |
| `monthly` | every 1 to 12 months | a day of month, an hour and a timezone, all three or none |

`manual` means the dashboard only refreshes when a person or a tool asks it to.
It is not a degraded state: a dashboard with no schedule keeps its URL, its
versions, its thumbnail and its share link forever.

The hourly multipliers are the divisors of 24, so "every 5 hours" is refused. The
reason is that a divisor keeps the daily hour grid clean and repeating: every 6
hours means the same four times every day, where every 5 hours would drift
through the day and mean a different set of times each day.

**Day of month is capped at 28.** This is a deliberate clamp, not an oversight:
you cannot ask for the 31st, because February would silently skip. If you need
the end of the month, take the 28th.

Days of the week are numbered from 0 for Sunday.

**Hourly is the fastest cadence.** There is nothing below it: no every-15-minutes
and no every-30-minutes.

## Anchors and timezones

An anchor pins a cadence to a wall-clock moment: "daily at `09:00` in
America/New_York" rather than "every 24 hours from whenever this was armed".

Timezones are real IANA zone names. The anchor is interpreted in that zone, and
the conversion to an absolute instant happens after the period arithmetic, which
is what makes daylight saving behave correctly.

Without an anchor a cadence falls back to a plain interval from the last run:
plus one day, plus seven days, plus thirty days. Note that unanchored monthly is a
flat 30 days rather than a calendar month.

## The precision you actually get

This is the part most likely to look like a bug, so it is worth stating plainly.

**Dashies schedules to the minute you pick and starts the run within about half
an hour of it.** Usually much sooner.

Two things separate the nominal time from the actual one:

**The sweep grid.** The scheduler sweeps every 15 minutes, so a `09:00` run starts
at the first sweep at or after `09:00`.

**Anti-herd jitter.** Each dashboard carries a stable offset of 0 to 14 minutes,
derived from its own id. It exists so a thousand dashboards all set to "daily
`09:00`" do not hit their warehouses simultaneously at `09:00`. It is stable per
dashboard, so a given dashboard always drifts by the same amount.

The jitter lives only in the sweep's selection, never in the stored next-run
time. The "Next refresh" the web app shows you is the **nominal** instant, exactly
`09:00`, with no jitter baked in, so the displayed value and the stored value agree
byte for byte.

The practical reading: a dashboard ten minutes past its nominal time is healthy.
The web app agrees with you about this and does not call a run late until it is
more than 20 minutes past, precisely because a perfectly healthy dashboard is
legitimately past its nominal time for up to fifteen minutes every cycle.

Every cadence uses the same grid.

If you need a number to the second, refresh on demand rather than tightening the
cadence.

## Daylight saving

For an anchored schedule, the period is added on the **local date** and only then
converted to an absolute instant. That gives the behaviour you want:

- **"Daily `09:00` America/New_York" stays `09:00` local** across a daylight-saving
  change. It does not drift to `08:00` or `10:00`.
- On the transition day itself the real gap between two runs is 23 or 25 hours.
  That is the correct consequence of keeping the wall-clock time fixed, and it is
  accepted rather than corrected.
- A wall time that does not exist, in the spring-forward gap, or that happens
  twice, in the autumn repeat, is normalized silently. **A run is never lost and
  never doubled.**

An unanchored hourly schedule is unaffected, because it is defined on
absolute instants rather than wall-clock times.

The web app's preview of the next run implements the same conversion, so the time
it shows you and the time the run fires agree across a transition.

## What happens when a run fails

A failed scheduled run is not abandoned and not retried immediately. The next
attempt is pushed out by **5 minutes doubled per consecutive failure**, capped at
one full period of the dashboard's own cadence. So the retries go 5 minutes, 10,
20, 40, and so on, and a permanently broken daily dashboard settles into retrying
about once a day rather than ninety-six times.

A success resets the counter and re-anchors the schedule normally.

A failing dashboard stays in the sweep. It is the backoff, not exclusion, that
bounds the retry rate, which is why pausing is the way to stop retries rather
than waiting for it to give up.

Two other outcomes are not failures and are labelled differently:

- **No change.** The run succeeded and the numbers came back identical. Your data
  genuinely did not move.
- **Skipped.** The write collided with a republish that landed between the read
  and the write. Nothing is lost; the next run picks up the new body.

## Refreshing on demand

Three ways to ask for a refresh now: the web app, your AI tool, and a headless
HTTP endpoint. All three run the same recompute, all three require a paid plan,
and all three share one property worth relying on: **an on-demand run never
pushes out the scheduled slot.** Refreshing at `08:55` does not cancel the `09:00`
run.

Two differences to know:

- **All three work on a workspace dashboard.** Refreshing rewrites the
  dashboard's data, so a team dashboard needs the same seat as republishing it
  and a view-only member is refused. The paid plan that matters there is the
  workspace's, not your own.
- The AI tool and the HTTP endpoint **coalesce**, for two different reasons.
  They do not start a second run when one is **already in flight** for that
  dashboard, whatever started it, including a scheduled run; and they do not
  re-run when one **finished within the last minute**. A recent **failed**
  attempt does not coalesce, because the data is still stale and reporting it
  fresh would be a lie. Use `get_refresh_status` to see which of the two
  happened: it shows the live run, the surface that started it, and whether it
  is running the current version of the dashboard.
- **Nothing runs two refreshes of one dashboard at once, on any surface.** The
  web app's "Refresh now" included: two clicks five seconds apart do not run
  twice, because the second one joins the first rather than starting beside it.
  Only the 60-second window above is surface-specific.

The receipt you get back is metadata only: how many rows, how long it took,
whether the numbers changed. Never the data itself.

## Next

[Plans and what is gated](/concepts/plans-and-gating), since most of this page
needs one.
