dashboards

Send this email-report schedule now, on the caller's own session.

Sends THIS schedule's report immediately (DVT-4266), using its stored recipients, page scope and subject. Requires dashboard:write, and — because it delegates to the very same handler as POST /v1/dashboards/{id}/email — also data:query + data:export and a live Snowflake caller session, including that route's pinned- query-role behavior: on a dashboard with a pinned query role, the two email CALLs run under that pinned role instead of the caller's own (see that route's description; DVT-4345). It can therefore never egress anything the caller could not have emailed by hand.

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. Until they are run, the schedule is stored and previewed but 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 identity to read the data as without dvt holding a standing IMPERSONATE ON USER grant (ADR-0069, DVT-4300).

The send a user triggers here is EXACTLY the send the task makes, chart images included either way — the render is server-side, from the rows dvt already holds, under the same limits: 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. Two differences are real and worth stating rather than implying. First, row provenance. For a QUERY-BOUND panel, an unattended chart is drawn from the rows the task staged for that run, capped per panel, where a POST .../run chart re-queries live at full fidelity. For a panel carrying its own inline data.rows there is no re-query on EITHER path — both render those rows, and the only row-provenance difference is the unattended path's per-panel row cap; dvt never asks the task to re-read such a panel and stores no snapshot of a query result. Second, 3D/WebGL chart types (chart:*3d, chart:*-gl, chart:surface) always show their note on the unattended path, because a headless render context has no reliable WebGL. Every status code and body below is the email route's verbatim — including its 409 email-not-configured, 413 email-too-large, 502 report-unavailable, 504 report-timed-out and 504 email-send-unconfirmed — rather than a second vocabulary for the same outcomes.

Side effects: lastOutcome is stamped — with the same outcome string the dashboard.email audit row records whenever the send reached an audited outcome — and that audit row additionally carries meta.schedule_id (an ADDITIVE key — the frozen 10-key meta and the six frozen outcome spellings are unchanged) so scheduled sends are separable from interactive ones. lastRunAt is NOT moved by Run now: it records scheduled (unattended) fires only. A caller-scoped refusal (401/403/404) leaves lastOutcome UNCHANGED: those say the calling principal may not send, not that the schedule cannot, and the schedules list shows lastOutcome to every viewer. A 400/409/413/5xx after the schedule is resolved stamps not-sent-not-attempted unless the send path audited an outcome; a disabled schedule (409) is not run and is not stamped.

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

Request

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

Response

Email accepted and sent.

Changes

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