Scheduled reporting

Reports that run themselves — and re-check themselves

Set the cadence once. Every run re-validates the sheet's structure, pulls with the right date semantics, appends safely and posts the summary — with history, alerts and a run-now button.

A schedule is a promise that the numbers will be there before anyone asks. Keeping that promise is less about cron and more about what each run verifies: the right period, the same structure as last time, a write that can't land twice.

Date semantics that match how marketers talk

Schedules resolve periods the way your team means them, in the report's timezone:

  • "Previous week" — the last full ISO week, Monday to Sunday, in the app's timezone
  • "Yesterday" — yesterday in the account or app timezone, not UTC's opinion of it
  • "Month to date" — the 1st through yesterday, refreshed daily
  • "Last month" — the full calendar month, run after the platforms' restatement window settles

Timezone is resolved per source — AppsFlyer runs on the app's configured timezone, Meta on the ad account's — so a 'day' means the same thing everywhere in the report.

What every scheduled run does

  1. Re-validate the schema — confirm tabs, headers, sections and the append anchor still match the stored mapping; halt and alert on drift
  2. Pull fresh data — all sources, with the client's filters and a trailing re-pull window for platforms that restate (Meta, Google)
  3. Reconcile — platform spend against MMP attribution, variance computed
  4. Check the period — refuse to append a week that already exists
  5. Append — the validated write, formulas extended
  6. Notify — Slack or email summary, with out-of-range metrics flagged

Scheduled writes stay within the constraints you approved at setup; anything outside them — drift, anomalies, a failed source — stops the run and pings you instead of guessing.

Failure handling that doesn't hide failures

  • Source API down or timing out — retried with backoff; if it stays down, the run halts with a clear alert rather than writing a partial week
  • Schema drift detected — run halts, you get the diff of what changed, nothing is written
  • No partial writes, ever — a run either completes the validated append or leaves the report untouched
  • Anomaly flags — a metric outside its expected range gets called out in the summary, not buried in row 214

Run history, pause, resume, run-now

Every schedule keeps its receipts:

Per-schedule controls
Historyevery run: when, what was pulled, what was written, who triggered it
Pause / resumefreeze a client's schedule during a restructure without losing the configuration
Run nowtrigger the same validated pipeline on demand — month-end early, a client call in an hour
Failure alertshalted runs notify the channel you choose, with the reason

Pausing, resuming and changing a schedule — safely

Schedules need lifecycle operations as disciplined as their runs. Pausing is one click and logged — used during client restructures, tracking migrations, or any week where writing would be worse than waiting. Resuming offers the backfill: the missed periods listed, each runnable through the full validated pipeline, in order. Changing a schedule — new cadence, new channel, an updated definition — is previewed like a write: here's the current specification, here's the proposed one, confirm. Every change lands in the schedule's history with who and when, which turns "why did the Tuesday report move to Monday?" into a one-line lookup instead of a Slack excavation. The lifecycle discipline matters because schedules outlive their creators: a report that has run for two years will be owned by three different people, and the specification plus its change history is the handover. Nothing about the system's behavior should live only in someone's memory — that's the standard the run ledger, the mapping versions and the schedule history are all built to. In practice this is what makes audits painless: when finance or a client asks how a report has been produced for six months, the answer is the specification, its change history and the ledger — three artifacts that already exist — rather than a reconstruction project. Governance, which sounded like overhead, turns out to be records that write themselves.

Backfills and gaps, without corruption

Real schedules meet real interruptions: a report paused for a client restructure, two missed weeks, a new report that needs history. Backfill is the same validated pipeline pointed backwards — "append weeks 21–23" runs each period through the identical checks (schema, duplicates, previews) and lands them in order under the right sections. Because the duplicate guard is period-aware, a backfill can never double an existing week, and because writes are append-only, it can never disturb the rows around the gap. History gets repaired the same way it gets written: boringly.

Multi-timezone rosters

An agency's Monday is several Mondays: the Dubai client's week closes at GST midnight, the Paris client's at CET, the app data resolves in each app's configured timezone regardless. Opera schedules per report in that report's timezone and resolves each source in its own — so "Monday 7am, last full week" means the locally-correct thing for every client simultaneously. The alternative — one server timezone for everyone — is how Sunday evening spend ends up in two different weeks depending on the client, which is the kind of error that takes a quarter to notice.

A week of runs, concretely

What one client's schedule actually executes:

  • Mon 07:00 — weekly run: Wk 24 appended to the client report, summary to their channel, reconciliation refreshed. Trailing re-pull updates Wk 23's still-settling Meta numbers.
  • Tue–Fri 08:00 — daily pulse: yesterday's spend and conversions vs the 7-day baseline to the internal channel; flags only when something crosses a band.
  • Thu 07:05 — the weekly's trailing window has converged; no message, just the report quietly correct.
  • Jul 5, 07:00 — monthly close: June re-pulled whole (restatements settled), reconciled, appended to the monthly section, summary skeleton delivered for the human narrative.

Four cadences, one stored specification, zero calendar reminders.

The run ledger, line by line

Every schedule keeps receipts you can actually read:

Wk 24 · Mon 07:00 — ✓ completed · schema ok · 4 pulls · appended row 31 · summary sent Wk 25 · Mon 07:00 — ✓ completed · schema ok · trailing re-pull adjusted Wk 24 CAC $13.91 → $13.74 Wk 26 · Mon 07:00 — ⚠ halted · schema drift: column F header changed "CAC" → "CAC (USD)" · nothing written · alert sent Wk 26 · Mon 09:12 — ✓ run-now after mapping confirmed · appended row 33

The Wk 26 pair is the system working as designed: a human renamed a column, the run refused to guess, the fix took two minutes, and the report's history contains zero corrupted rows.

Different clients, different rhythms

Each client or team gets its own cadence, format and channel — a daily 8am pulse to #growth for one, a Monday 7am client report for another, a month-end roll-up for a third — all isolated, all logged, all running the same validated pipeline.

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

Safe enough for production

Opera is built to touch production reports and live ad accounts without breaking anything:

  • No destructive writes. Updates are append-only by default — your existing data and formulas are never overwritten.
  • Preview before execution. You see exactly what Opera will change before a single cell is written.
  • Campaigns paused by default. New campaigns are created paused, with approvals required before any spend.
  • Full audit logs and client-level isolation. Every action is logged, and each client's data and rules stay separate.

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

What happens if my sheet's structure changes between runs?
The run halts before writing, and you get an alert showing what drifted — renamed columns, moved sections, inserted rows. Opera never writes into a structure it can't verify.
Can a scheduled run append the same week twice?
No — the duplicate-period check refuses a period that already exists in the report, even if the schedule fires twice.
How do you handle Meta and Google restating recent conversions?
Every run re-pulls a trailing window so restated days converge in your report. Month-end runs can be timed after the restatement window settles.
Can different clients run on different schedules and timezones?
Yes — cadence, timezone, format and delivery channel are per client, fully isolated, with their own run history.
Can it backfill missed periods?
Yes — backfills run the identical validated pipeline per missing period, in order, with the same duplicate and drift guards, and they're recorded in the run ledger like any run.
What if I need the report before the schedule fires?
Run-now triggers the identical validated pipeline on demand — same checks, same preview policy, same audit entry.

See exactly what Opera would automate in your workflow.

A 45-minute teardown of how you report today: we map every step, mark what Opera automates, and send you the written spec — useful whether or not you buy.