Recipe: Scheduled Report Pipeline
This recipe produces a report on a timer and delivers it to your team. A schedule fires on a cron expression, a sql or timeseries query pulls the numbers, a document renders them into a readable page, and an email or slack step sends it out — every morning, with no one pressing a button.
The problem it solves
Recurring reports are the classic “someone runs a query and pastes it into an email on Mondays” task. It is easy to forget, easy to do inconsistently, and impossible to audit. This recipe makes the whole chain a declarative pipeline: the schedule guarantees it runs, the query guarantees the same numbers, and the delivery step guarantees it lands in the same place every time.
Elements
| Element | Role |
|---|---|
schedule | Fires the pipeline on a cron expression. |
sql / timeseries | Pulls the report data — relational rows or time-bucketed metrics. |
document | Renders the results into an HTML/markdown report page. |
email / slack | Delivers the finished report. |
automation | Sequences query → render → deliver as one flow. |
Flow
- Create a
schedule. Setspec.cronto your cadence (e.g.0 0 8 * * *for 8am daily); it emits a schedule event downstream when it fires. Usetrigger_nowto dry-run the pipeline without waiting, andnext_runsto confirm the upcoming fire times. - Pull the data. For relational reporting, use the
sqlelement’squeryoperation with bind parameters. For metrics over time, use thetimeserieselement’squery(aggregated) orrange(raw points), ordownsamplefor a rolled-up series. - Render the report with a
document:writethe results into a markdown page, thenrenderit to HTML (markdown plus tables, code highlighting, and math). - Deliver it. Use the
emailelement’ssend(orsend_trackedfor open/click events) to mail the report, or theslackelement’ssendto post it to a channel. - Tie the chain together in an
automation, which referencesschedule,sql,timeseries,document,email, andslackas steps. The schedule event starts the run; the rest follow as ordered steps.
What this shows
Each stage is a real element doing the one thing it is good at: the schedule owns when, the data elements own what, the document owns how it reads, and the connector owns where it goes. Swap monthly for daily by editing one cron field; switch delivery from email to Slack by re-pointing one step — the query and the rendering do not change. Because the pipeline is declarative, “did the report run?” is answerable from the schedule’s history, not from someone’s memory.