---
title: Set and change a refresh schedule
description: Pick a cadence and an anchor time for a dashboard, understand when a run actually fires, and know what happens after a failed run.
updated: 2026-08-04
tier: pro
---

A published dashboard re-runs its own SQL on the cadence you set, with no AI in
the loop. This page is how to set that cadence, change it, and read what happens
afterwards.

:::warning{title="Any non-manual cadence needs a paid plan"}
Arming an automatic schedule is refused on a free plan with
`a paid plan is required to schedule automatic refresh`. See
[what is gated](/concepts/plans-and-gating#what-is-gated).

Setting a schedule **back to manual is always allowed**, on any plan.
:::

## Two places to set it

| Where | What it can set |
|---|---|
| The **Schedules** page at [dashies.xyz/app/schedules](https://dashies.xyz/app/schedules) | Everything, including the sub-hourly cadences. |
| Your AI tool, via `set_refresh_schedule` | Everything except sub-hourly. |

Your AI can also set the coarse cadence at publish time, in the spec's
`source.schedule`. That takes `manual`, `hourly`, `daily`, `weekly`, or
`monthly`, and you refine the exact timing afterwards.

:::note{title="Sub-hourly is a web-app-only setting"}
The `set_refresh_schedule` tool takes no sub-hour argument, so an AI cannot set
every 15 or 30 minutes however confidently it offers to. Use the Schedules page.
:::

## Cadences, intervals, and anchors

| Cadence | Interval allowed | Anchor |
|---|---|---|
| `manual` | none | none. It runs only when triggered. |
| Sub-hourly | every 15 or 30 minutes | none. Runs on an absolute UTC grid. |
| `hourly` | every 1, 2, 3, 4, 6, 8, or 12 hours | optional hour, which then needs a time zone |
| `daily` | every 1 to 30 days | optional hour and time zone |
| `weekly` | every 1 to 4 weeks | day of week, hour, and time zone: all three or none |
| `monthly` | every 1 to 12 months | day of month, hour, and time zone: all three or none |

The model behind this table is in
[cadences and intervals](/concepts/schedules#cadences-and-intervals).

Anchor values:

- **Hour** is 0 to 23.
- **Day of week** is 0 to 6, with **0 meaning Sunday**.
- **Day of month** is **1 to 28**.
- **Time zone** must be a real IANA zone name, such as `America/New_York`.

Three of those constraints surprise people, so here is why each exists:

- **Hourly takes only divisors of 24.** "Every 5 hours" is rejected, because a
  non-divisor drifts across the daily hour grid instead of repeating cleanly.
- **Day of month stops at 28.** If you could ask for the 31st, February would
  silently skip. The clamp guarantees every month has the day you picked.
- **A sub-hourly schedule takes no anchor at all.** It runs on an absolute UTC
  minute grid, at `:00/:15/:30/:45` or `:00/:30`, which makes it immune to time
  zones and daylight saving by construction. It is also **personal dashboards
  only** today.

## When a run actually fires

The displayed next run is the **nominal** time. Runs are claimed on a grid, and
each dashboard carries a small fixed offset so that thousands of dashboards
sharing an `09:00` anchor do not all fire in the same instant.

The practical consequence, worth knowing before you file a bug:

- An anchored schedule fires within roughly **30 minutes** of its nominal time.
- A sub-hourly schedule is tighter, within a few minutes.
- A dashboard sitting slightly past its displayed next run is normal, not late.

The offset is never shown, because it is not a property of your schedule. See
[the precision you actually get](/concepts/schedules#the-precision-you-actually-get).

## Daylight saving

An anchored schedule keeps its **local** wall-clock time across a transition.
`Daily 09:00 America/New_York` stays `09:00` local; it does not drift to `08:00`
or `10:00`.

On the transition day itself, the real gap between two daily runs is 23 or 25
hours. A wall time that does not exist in spring, or happens twice in autumn, is
normalized rather than lost or doubled. The next-run time the app shows and the
time the run actually fires agree across the boundary.

Sub-hourly and unanchored hourly schedules are unaffected, because they are
absolute instants. The policy in full:
[daylight saving](/concepts/schedules#daylight-saving), and
[anchors and time zones](/concepts/schedules#anchors-and-timezones).

## Running one now

The Schedules page has a **Run now** control on every dashboard.

:::note{title="Your AI can only trigger personal dashboards"}
The `trigger_refresh` tool is personal-only today:

```bash
Triggering a refresh on demand isn't available for workspace dashboards yet - it works on personal dashboards only.
```

Use **Run now** in the web app for a workspace dashboard.
:::

:::warning{title="On-demand refresh needs a paid plan, and who pays differs by scope"}
For a **personal** dashboard the payer is you:

```json
{ "ok": false, "error": "Refreshing on demand requires a paid plan.", "code": "tier_required" }
```

For a **workspace** dashboard the payer is whoever connected the data source,
who may be someone else entirely:

```json
{ "ok": false, "error": "This dashboard's data source is on a plan that no longer includes refresh. The member who connected it needs an active paid plan.", "code": "tier_required" }
```

If you hit the second one, upgrading your own plan will not clear it. Find the
member who created that data source.
:::

Repeat triggers within 60 seconds of a previous attempt short-circuit and report
back that they were coalesced, **unless the last attempt failed**. A pipeline
retrying seconds after a failure genuinely re-runs, because the data is still
stale.

## After a failed run

A failed run does not strand a dashboard, and it does not retry at full rate
either.

- Retries back off at **5 minutes, then 10, 20, 40, 80**, doubling until they cap
  at one full period of that dashboard's own cadence. A permanently broken daily
  dashboard retries about once a day rather than 96 times.
- On the next success, the failure count resets and the schedule re-anchors
  normally.
- In the UI a failing schedule reads **Retrying** with the actual retry time,
  rather than a red Failed sitting beside a blue Due now.

Pausing or archiving the dashboard is how you stop the retries.

### Failure alert emails

You get an email once a dashboard has failed a chosen number of times in a row.
Set the threshold in Settings, then Alerts: the options are **2, 3, or 5**, and
the default is 3.

- One email per outage, not one per failed run, plus a reminder roughly every 7
  days while it stays broken. A monthly dashboard reminds every 30 days.
- One recovery email on the next run that records a real success.
- Alerts can be muted per dashboard from the schedule dialog. A muted dashboard
  never alerts, and unmuting mid-outage does not retroactively fire.
- A free plan never receives the email, but the outage is still tracked, so
  upgrading during one alerts on the next failure rather than swallowing it.

## If your plan lapses

Refresh does not fail and nothing is deleted. Each due run records a **skipped**
run with the reason `tier_lapsed`, the schedule is preserved, you get one notice
email, and re-subscribing resumes it automatically.

A `tier_lapsed` skip does not send a failure alert, and it does not count as the
recovery that closes an outage.

## Check it worked

1. Open [dashies.xyz/app/schedules](https://dashies.xyz/app/schedules). Your
   dashboard shows the cadence you set and a **next run** time.
2. Wait for that time to pass, plus the half hour described above, then open the
   History tab. A run should be listed.
3. A successful run marked **No change** is a healthy result, not a failure. It
   means the SQL re-ran and produced byte-identical numbers.
4. If nothing appears at all, check the dashboard is actually refreshable: ask
   your AI for its refresh status. A dashboard with no schedule and no next run
   was published without a working refresh manifest, which is usually a data
   source scope problem. See [Work as a team](/guides/work-as-a-team).
