Skip to content
Joe BI Platform
Esc
navigateopen⌘Jpreview
On this page

Scheduled reports

The report delivery service - report types, cadences, formats, subscriptions, and send-now.

Scheduled reports

The report service replaces Looker’s ~140 scheduled plans. It runs inside the data-plane Worker: a cron wakes every 5 minutes, finds due subscriptions, renders each report from the same data the dashboards use, and emails it via Resend as reports@joe.coffee.

Design properties worth knowing:

  • Idempotent. Each send is claimed against a period key (the business date or month), so a retry after a failure cannot double-send.
  • No-data suppression. A report with nothing to say (no payouts on a Sunday, no orders yesterday) sends nothing rather than an empty email.
  • Branded and linked. Every email uses the shared Joe layout; merchant reports carry a “View live dashboard” button backed by a 90-day, shop-locked /e/ link. Clicking it mints a fresh short-lived Metabase token, so the button no longer inherits the old seven-day embed-token window. Internal reports still link directly into Metabase.
  • Formats: inline HTML, CSV, XLSX, rendered dashboard PDF (via the joe-render service), and webhooks. Every format Looker used is supported.

Report types

Type Cadence Contents
weekly_sales Fridays Branded email: net sales, orders, average ticket, tips, week-over-week, top items
weekly_sales_csv Fridays Same week as CSV + XLSX attachments
shop_sales_pdf Fridays The shop’s live dashboard rendered to a branded PDF
daily_snapshot Daily Yesterday vs same weekday prior week, top items
daily_orders_csv Daily at each shop’s close Order-level CSV of today (live path)
monthly_orders_csv 1st of month Last complete month, order counts by store/day/source
payout_arrivals Daily (banking days) Payouts arriving tomorrow
ladro_iif_daily_sales_csv Daily Caffe Ladro QuickBooks IIF daily sales journal
ladro_iif_transfers_csv Daily Caffe Ladro QuickBooks bank-transfer entries (byte-parity with Looker verified)

New types are added as small code packages (see Requesting a new report).

Cadence model

Three scheduling shapes cover the whole Looker inventory:

  1. Fixed-hour (default 13:00 UTC) - weekly and daily reports without a specific hour.
  2. Per-shop send hour (send_hour_utc on the subscription) - the 5-minute cron gates on the current UTC hour; used for “at store close” reports.
  3. Monthly (monthly_1st) - fires on the 1st, keyed by month.

Managing subscriptions

Admin page: https://data-plane.joe.coffee/v1/reports/admin (admin token required).

A subscription = shop + report type + cadence + recipients (+ optional send hour). Create, deactivate, and inspect from the admin page, or via the API:

GET  /v1/reports/subscriptions            list
POST /v1/reports/subscriptions            create {shopId, reportType, recipients, sendHourUtc?}
POST /v1/reports/subscriptions/:id/deactivate

Send-log entries record every delivery (or suppression) per subscription per period, which is what the dual-send soak reads to compare against Looker.

Send-now (test a report immediately)

POST /v1/reports/send-now
{"shopId": "...", "reportType": "weekly_sales", "recipients": ["you@joe.coffee"]}

Renders and sends immediately with current data; repeatable (send-now runs are not idempotency-logged). This is the fastest way to check a report against Looker’s version of the same day.

Two operational notes: fire send-nows one at a time (a rapid burst can contend on the Worker’s DB pool), and the first PDF render after idle can take ~20 extra seconds while the render machine wakes.

Failure handling

Failed sends surface in the send log with the error; the claim design means fixing the cause and re-running never double-delivers. If Resend is misconfigured (missing key), the service refuses to claim periods at all, so nothing is silently marked sent.

Was this page helpful?