dashboards

Create an email-report schedule (Snowflake native app only).

Stores a cadence, timezone and recipient list for emailing this dashboard (or one of its pages) as an inline HTML report (DVT-4266). Snowflake-native only — the routes are not mounted outside snowflake mode, so every verb here 404s there. Requires dashboard:write.

THIS ROUTE ANSWERS 404 feature-disabled ONLY WHEN THE SURFACE IS OFF. Every verb under /v1/dashboards/{id}/email-schedules shares one gate — create, list, get, patch, delete, run and setup — the editionCapabilities.unattendedSchedules cell. DVT-4490 ships that cell TRUE wherever emailSchedule is true, the normal Snowflake native-app case, so a shipped install answers normally here, not 404. When the refusal does fire it precedes the path parse and the authorization load, so it is identical for a real schedule id and an invented one.

404 survives for exactly two operator-facing reasons: DVT_UNATTENDED_SCHEDULES_ENABLED=false set on BOTH dvt-api and dvt-engine (an explicit opt-out, not the shipped default), or an edition where emailSchedule itself is false (gallery/enterprise — the routes are not even mounted there). Stored rows are unaffected either way — this is a switch, not a migration.

POST /v1/dashboards/{id}/email ("Email me this report now") remains the on-demand alternative regardless of this cell. Clients must gate every schedule affordance on the capability cell rather than assume a fixed answer from this route's existence. The paragraph below describes the behavior every non-disabled install returns.

Behind the cell: schedules fire on their own once a Snowflake admin creates their task. GET .../{sid}/setup returns the exact statements; taskState reports what dvt has observed: absent until a run arrives, confirmed once one has. Until an admin runs them the schedule is stored and previewed and nothing arrives; POST .../run always sends immediately on the caller's own live session. No ticker runs, in any edition - an unattended send is driven by a CONSUMER-OWNED Snowflake task, which is what gives it a real Snowflake identity without dvt holding a standing IMPERSONATE ON USER grant (ADR-0069 Amendment 2, DVT-4300). A scheduled report embeds the same chart images an interactive send does — the render is server-side, from the rows dvt already holds: the ones the consumer's task staged for this run, plus any panel carrying its own inline data.rows in the spec (the backend-free shape, whose query is kept for the SQL inspector and is never executed — dvt stores no snapshot of a query result and never asks the task to re-read such a panel). Same limits either way: at most 12 chart images per report (charts past the email's render time budget or its body budget are noted in place), and a chart that fails falls back to its "view in dvt" note instead.

Supply either a raw 5-field cron or a preset — not both. nextRunAt is computed server-side from (cron, timezone) in the same transaction, so the SPA's client-side "next sends" preview can be cross-checked against it. Recipients are validated by exactly the same rules as POST /v1/dashboards/{id}/email.

A timezone that cannot be resolved to an IANA zone is a 422 (DVT-4321), not a 400; the failing value is echoed in a timezone extension field. A blank timezone, or "Local", normalizes to UTC rather than being rejected. The cadence must also fire no more than once every 15 minutes (DVT-4794, ADR-0069 Amendment 5 slice S3, D5) — a finer cadence is 422 cadence-too-fine, not stored.

post/v1/dashboards/{id}/email-schedules

Request

  • Base URL: http://localhost:8080
  • URL: http://localhost:8080/v1/dashboards/{id}/email-schedules
  • Auth: one of:
    • API key in cookie dvt_session
    • HTTP bearer

Request body

titlestring

Optional human-readable label for this schedule.

cronstring

A canonical 5-field cron expression (validated server-side). The API accepts ANY valid 5-field expression, and preset accepts hourly as well as the three the UI offers — the dvt SPA only ever sends daily / weekly-on-weekday / monthly-on-day presets, but an API or agent caller is not restricted to those. Whatever cadence is stored here is the one the generated CREATE TASK reproduces, and dvt refuses a run that does not land on an occurrence this expression names (no-scheduled-occurrence) — so changing it means re-pasting the statements from GET .../{sid}/setup.

timezonestring

IANA timezone name; the cadence is evaluated in this timezone, and nextRunAt is returned in UTC. The SPA defaults it to the browser's zone. A blank value, or "Local", normalizes to "UTC"; any other name that cannot be resolved is a 422.

pageIdstring nullable

When set, the report covers only this page (by spec slug). Null or absent emails every non-hidden page (hidden pages are excluded, ADR-0036 §1; if every page is hidden, a run fails with bad page scope) — the same semantics as pageId on POST /v1/dashboards/{id}/email.

subjectstring

Optional subject override; empty means the server default.

recipientsstring[] required

The full recipient set (set semantics — a later PATCH with recipients REPLACES it). Validated by exactly the same rules as the send route: 1-50 unique, bare addresses with no display name. Snowflake applies the binding check at SEND time — every recipient must be a user of the consumer's own account with IS_EMAIL_VERIFIED — and if one fails, NOTHING is sent. That check is per-principal and cannot be anticipated here; use "Run now" to confirm a recipient list actually delivers.

Example request

{
  "cron": "0 7 * * 1",
  "timezone": "America/New_York"
}

Response

Schedule created.

Changes

No recorded changes to this operation across all 1 revision of this API.