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 it only authorizes ad accounts you own (or accounts added as test users). 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 search_console_raw, the lossless Search Console altitude, because page-level detail is platform-specific and doesn't belong in the cross-channel layer:
tablebi pin --title "SEO monthly report" \ --widget "Impressions by day (28d)::line=SELECT date, SUM(impressions) AS impressions FROM search_console_raw WHERE date >= (SELECT MAX(date) FROM search_console_raw) - 28 GROUP BY date ORDER BY date" \ --widget "Clicks by day (28d)::line=SELECT date, SUM(clicks) AS clicks FROM search_console_raw WHERE date >= (SELECT MAX(date) FROM search_console_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_raw WHERE date >= (SELECT MAX(date) FROM search_console_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, ad_group, SUM(cost) AS spend FROM facts WHERE date >= (SELECT MAX(date) FROM facts) - 14 GROUP BY platform, campaign, ad_group 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's a real pinned dashboard, built exactly this way, running on live Search Console data (open any widget's "ask" button and you can send the author a question about it):