Marketing report templates you can pin as live dashboards
Searching for a marketing report template usually gets you a nicely designed document with empty charts — and the layout was never your problem. The problem is the refill: every week or month, someone pipes fresh numbers into the old frame, checks the totals, fixes the chart that broke, and ships a file that's stale on arrival. This page takes a different approach. Below are three marketing report templates defined not as documents but as widget lists — a title plus a SQL query per widget — that you pin once with a single command. Each becomes a live dashboard URL that refreshes itself. The template stops being a chore you repeat and becomes a report that maintains itself.
The template was never the work — the refill is
Be honest about where reporting time actually goes. Picking a layout happens once. What repeats, forever, is:
- Exporting this period's numbers from every platform, in every platform's own format.
- Pasting them into the template's spreadsheet or slides and repairing whatever shifted.
- Re-checking definitions — was ROAS computed off the same conversion value column as last month?
- Re-sending the file, and answering "which version is current?" three days later.
A document template can't fix any of that, because a document is a snapshot by nature. The fix is to change what the template is.
A marketing report template is just titles + SQL
On TableBI — a data backend built to be driven by an AI agent like Claude Code (Anthropic's terminal-based coding agent) — a report template is literally a list of widgets. Each widget is "Title=SQL" for a table, or "Title::line=SQL" for a line chart. One tablebi pin command publishes the list as a dashboard with a public, read-only URL that re-pulls from your connected sources automatically — on access and on a schedule. Definitions come from shared macros (roas(), ctr(), cpa()…), so the arithmetic is identical in every report, every period, no matter who pins it.
Setup, if you haven't already:
npm i -g @tablebi/cli
tablebi login
tablebi install
# connect whichever sources the template needs (OAuth in browser)
tablebi connect gsc --site sc-domain:example.com
tablebi connect ga4
tablebi connect meta_ads
tablebi connect google_ads
Meta Ads is in beta. The Meta app hasn't cleared App Review yet, so today only people added to it as test users can authorize their ad accounts. Everything else on this page is live.
Now the three templates. Each is a complete command — copy, paste, done. Your agent can also adapt any of them from a plain-English request ("add a country breakdown"), which is the intended workflow.
Template 1: weekly marketing overview
When to use it: the standing Monday view — is anything up, down or on fire across all channels. Who it's for: you, your team standup, or a founder who wants one link instead of four platform logins.
tablebi pin --title "Weekly marketing overview" \ --widget "Clicks by day (28d)::line=SELECT date, SUM(clicks) AS clicks FROM facts WHERE date >= (SELECT MAX(date) FROM facts) - 28 GROUP BY date ORDER BY date" \ --widget "Spend by platform (28d)=SELECT platform, SUM(cost) AS spend FROM facts WHERE date >= (SELECT MAX(date) FROM facts) - 28 GROUP BY platform ORDER BY spend DESC" \ --widget "Top campaigns by spend (28d)=SELECT platform, campaign, SUM(cost) AS spend, SUM(conversions) AS conversions FROM facts WHERE date >= (SELECT MAX(date) FROM facts) - 28 GROUP BY platform, campaign ORDER BY spend DESC LIMIT 10" ✓ published → https://<your-ws>.tablebi.com/d/dsh_… (public, read-only, self-refreshing)
Note the date idiom used throughout: date >= (SELECT MAX(date) FROM facts) - 28 anchors the window on the freshest data actually present, not today's calendar date — so a platform reporting a day behind never punches a fake dip into your charts.
Template 2: SEO monthly report
When to use it: the monthly organic check-in — search trend and which pages carry it. Who it's for: whoever owns content and SEO, or a client who asks "is the blog working?"
This one runs on the Search Console tables rather than the cross-channel layer, because page-level detail is platform-specific. The daily trend reads search_console_totals_raw (the same totals as the Search Console overview) and the page list reads search_console_pages_raw; neither is summed from the per-query table, which leaves out every query Google anonymises:
tablebi pin --title "SEO monthly report" \ --widget "Impressions by day (28d)::line=SELECT date, SUM(impressions) AS impressions FROM search_console_totals_raw WHERE search_type = 'web' AND date >= (SELECT MAX(date) FROM search_console_totals_raw) - 28 GROUP BY date ORDER BY date" \ --widget "Clicks by day (28d)::line=SELECT date, SUM(clicks) AS clicks FROM search_console_totals_raw WHERE search_type = 'web' AND date >= (SELECT MAX(date) FROM search_console_totals_raw) - 28 GROUP BY date ORDER BY date" \ --widget "Top pages by clicks (28d)=SELECT page, SUM(clicks) AS clicks, SUM(impressions) AS impressions FROM search_console_pages_raw WHERE date >= (SELECT MAX(date) FROM search_console_pages_raw) - 28 GROUP BY page ORDER BY clicks DESC LIMIT 15" ✓ published → https://<your-ws>.tablebi.com/d/dsh_…
Every pinned dashboard also carries the trust context the queries ran with — source freshness and caveats — which matters here because Search Console's numbers arrive on a platform-specific lag, and a report that silently pretends otherwise invites the wrong conclusions.
Template 3: paid efficiency review
When to use it: weekly or before any budget conversation — where paid money works, where it doesn't, and what's burning with nothing to show. Who it's for: the person who moves budgets, and anyone who has to defend the ad spend line.
tablebi pin --title "Paid efficiency review" \ --widget "ROAS by platform (28d)=SELECT platform, SUM(cost) AS spend, roas(SUM(conversion_value), SUM(cost)) AS roas FROM facts WHERE cost > 0 AND date >= (SELECT MAX(date) FROM facts) - 28 GROUP BY platform ORDER BY spend DESC" \ --widget "CPA by campaign (28d)=SELECT platform, campaign, SUM(cost) AS spend, cpa(SUM(cost), SUM(conversions)) AS cpa FROM facts WHERE cost > 0 AND date >= (SELECT MAX(date) FROM facts) - 28 GROUP BY platform, campaign ORDER BY spend DESC LIMIT 15" \ --widget "Spending, zero conversions (14d)=SELECT platform, campaign, SUM(cost) AS spend FROM facts WHERE date >= (SELECT MAX(date) FROM facts) - 14 GROUP BY platform, campaign HAVING SUM(cost) > 0 AND SUM(conversions) = 0 ORDER BY spend DESC" ✓ published → https://<your-ws>.tablebi.com/d/dsh_…
The third widget is the one that pays rent: ad sets and campaigns that spent in the last two weeks and converted nothing, ranked by damage. Because the facts layer normalizes Meta, Google and CSV-imported channels into the same columns, this list is cross-platform for free — Meta's purchase value and Google's conversion value are both just conversion_value here.
What a pinned template looks like
Templates on a page are hypothetical. Here is a live dashboard on real data you can open now: Search Console totals across the TableBI founder's own sites. It is a different dashboard from the one described above, published the same way, and it re-runs its queries as the sources sync (what the demo shows, and what it doesn't):