Guide

Scheduled marketing reports: daily, weekly and monthly on autopilot

A practical, no-fluff guide for performance teams and agencies.

A scheduled report is a promise: the numbers will be there, correct, before anyone asks. Cron is the easy 5% of keeping it. The other 95% is semantics — what "last week" means, in which timezone, when a month is actually closed — and verification: what every run must check before it's allowed to write. This guide covers both.

Date semantics, the part everyone skips

  • "Previous week" = the last full ISO week, Monday–Sunday, in the report's timezone — not "the last 7 days," which double-counts and drifts
  • "Yesterday" = yesterday in the account or app timezone; a UTC yesterday splits days for most of the world
  • "Month to date" = the 1st through yesterday, refreshed daily, labeled as in-progress
  • "Last month" = the calendar month, closed only after the platforms' restatement window settles — Meta and Google keep editing the recent past for ~a week

Each source resolves in its own timezone (AppsFlyer per app, Meta per ad account) and lands in the report's. That sentence is the difference between a trend line and an artifact.

The three cadences, designed differently

Daily — a pacing pulse: yesterday's spend vs budget by campaign, posted to the team channel before standup. Optimized for glanceability and exception flags; never the place for cohort metrics.

Weekly — the workhorse: last ISO week appended under the current monthly section, KPIs computed, reconciliation refreshed, summary with WoW deltas to each audience's channel.

Monthly — the close: run after restatements settle, roll the weeks into the monthly view (or re-pull the month whole — pick one and write it down), and expect numbers that differ slightly from summed fresh weeklies, for explainable reasons.

What every run must verify before writing

  1. Schema — tabs, headers, sections and the append anchor still match the stored mapping; halt with a diff on drift
  2. Period — refuse a week that already exists in the report
  3. Sources — all pulls succeeded; a missing platform halts the run rather than shipping a partial week
  4. Trailing windows — recent days re-pulled so restating platforms converge
  5. Write — previewed, append-only, formulas extended

The single most important property: no partial writes. A run completes or leaves the report untouched and says why.

Failure policy worth copying

API down → retry with backoff → still down → halt + alert with the reason. Drift detected → halt + diff. Metric outside its expected range → write, but flag loudly in the summary. The system's job in every failure mode is to be loud and conservative — silence is the only unacceptable behavior.

Per-audience delivery

The schedule is also a routing table: the growth team's daily to #growth at 8am, client A's weekly to their channel Monday 7am, client B's on Tuesday because they asked — each isolated, each with its own history, pause/resume and a run-now for the day someone calls early.

QA checklist

  • ✓ Period boundaries verified against the timezone written in the report
  • ✓ Duplicate guard tested (fire the schedule twice; the second must refuse)
  • ✓ Drift halt tested on a copy
  • ✓ A deliberate source failure tested — confirm halt + alert, no partial row

When a schedule is premature

While the report's structure or definitions still change monthly. A schedule amplifies whatever it runs — stabilize first.

How Opera runs it

This is scheduled reporting end to end: semantics resolved per source, every run re-validated, no-partial-writes, per-client rhythms with history and alerts.

"Every Monday at 7am, update all client weekly reports and send each its Slack summary."

See this running on your own reports.A 45-minute workflow audit maps your current process and shows exactly what Opera automates — step by step.

Frequently asked questions

Why ISO weeks instead of 'last 7 days'?
'Last 7 days' is a moving window — it overlaps, drifts with run time, and makes WoW comparisons meaningless. A fixed Monday–Sunday week is comparable forever.
Why does my monthly total differ from my four weeklies summed?
Restatements: the weeklies were snapshots of settling numbers; the monthly ran after they converged. Decide whether the monthly re-pulls or rolls up, write it down, and the difference becomes explainable.
Should failed runs auto-retry the write?
Retry the *pull*, never blind-retry the write. The duplicate guard makes a re-run safe, but the failure should still alert — silent recovery hides systemic problems.

Watch Opera run a real workflow, end to end.

Three minutes: a plain-language request, a Sheet schema read, an AppsFlyer pull, a previewed append, a Slack summary — then a paused campaign launch.