Every Monday someone on your team spends 45 minutes opening Google Analytics, a revenue spreadsheet, and Stripe, copying numbers into a doc, and writing two paragraphs nobody is sure anyone reads. The numbers are always slightly stale, the format drifts week to week, and when that person is on vacation the report just doesn’t happen.
This tutorial replaces that ritual with a workflow that runs at 7:00 a.m. every Monday, pulls last week’s metrics from Google Analytics, a Google Sheet, and Stripe, has an LLM write a short narrative summary — actual analysis with week-over-week deltas and one recommended action, not a number dump — and delivers it to Slack and email before anyone’s first coffee.
The primary build is in n8n because fan-in from three data sources is cleaner there; a shorter Zapier variant follows for teams already living in Zapier. This is a beginner-friendly build: no code required in the n8n version, about 45 minutes end to end.
What you’re building
Schedule Trigger (Mon 07:00)
├── Google Analytics → sessions, users, conversions (this week vs last)
├── Google Sheets → your manually tracked KPIs
└── Stripe → charges for the last 14 days
↓ Merge
LLM (analyst prompt) → narrative report
↓
Slack #metrics + Gmail to stakeholders
The design principle: compute the numbers in the workflow, let the AI only write the words. If you ask an LLM to “look at Stripe data and tell me revenue,” it will occasionally arithmetic itself into a wrong number. If you hand it pre-computed values — “revenue this week: $4,820, last week: $4,210” — it just narrates, and narration is the thing LLMs are reliably good at.
Prerequisites
- An n8n instance (Cloud or self-hosted)
- Google account with access to GA4 and your KPI spreadsheet
- Stripe account with a restricted API key (read-only on Charges is enough)
- An OpenAI or Anthropic API key
- A Slack workspace
Step 1: Schedule Trigger
- Add a Schedule Trigger node.
- Trigger Interval:
Weeks, Trigger at Weekday:Monday, Trigger at Hour:7am. - Check the workflow’s timezone in Workflow Settings — n8n defaults to the instance timezone, which on a rented server is usually UTC. Set it explicitly to your local zone or your “Monday 7 a.m.” report arrives Sunday evening or Monday afternoon.
Step 2: Pull Google Analytics
- Add a Google Analytics node connected to the trigger.
- Credential: Google Analytics OAuth2 (the consent screen needs the
analytics.readonlyscope). - Resource:
Report, Property: select your GA4 property. - Date Range:
Last 7 Days. - Metrics: add
sessions,totalUsers,conversions. - Duplicate this node, rename it
GA Last Week, and set its date range to the prior 7-day window (use Custom with expressions{{ $now.minus({days: 14}).toFormat('yyyy-MM-dd') }}to{{ $now.minus({days: 8}).toFormat('yyyy-MM-dd') }}). Two snapshots give the AI real deltas instead of guesses.
Step 3: Pull your KPI sheet
- Add a Google Sheets node.
- Operation:
Get Row(s), pick your spreadsheet and the tab where you track KPIs (signups, churn, NPS — whatever you log manually). - If the sheet has one row per week, add a Filter or use the Sort + Limit options to grab the latest two rows.
If you don’t have a KPI sheet, skip this node — the workflow degrades gracefully and the template marks it optional.
Step 4: Pull Stripe revenue
- Add a Stripe node (or an HTTP Request node against
https://api.stripe.com/v1/chargesif you want pagination control). - Resource:
Charge, Operation:Get Many, Limit: 100. - Use a restricted key: in the Stripe dashboard, create a key with read-only access to Charges and nothing else. A leaked read-write Stripe key is a genuinely bad day; a leaked read-only one is merely embarrassing.
- Add a Summarize node after it: Aggregate
amountwithSum, grouped by nothing — that produces total revenue for the period. Add a second Summarize withCountfor transaction count. (n8n’s Summarize node does in one click what would otherwise be a Code node.)
Step 5: Merge everything
Add a Merge node in Combine → Combine All mode to pull the GA, Sheets, and Stripe branches into one item. With three sources, chain two Merge nodes (n8n merges two inputs at a time) — the template wires this for you. The result is a single item carrying every metric, current and prior period.
Step 6: The analyst prompt
Add an OpenAI node (or Anthropic — identical setup): Resource Text, Operation Message a Model. A mid-tier model is the sweet spot here; report-writing benefits from a better model than classification does.
System message — this prompt is the heart of the whole workflow, paste it exactly:
You are a sharp, concise business analyst writing the Monday metrics report for a small business team. You receive pre-computed metrics for the last 7 days and the 7 days before that. You NEVER recalculate or invent numbers — you only use the figures given.
Write the report in this exact structure:
**📊 Week of {{date}} — Weekly Report**
**TL;DR** — one sentence: is this week good, bad, or flat, and the single biggest reason why.
**The numbers**
- One bullet per metric: current value, then delta vs prior week as both absolute and percentage, with an arrow (↑ ↓ →). Round percentages to whole numbers.
**What happened** — 2-3 sentences interpreting the movement. Connect metrics where the data supports it (e.g., "sessions fell 12% but conversions held, so traffic quality improved"). If a delta is under 5%, call it flat — do not narrate noise as a trend.
**⚡ One thing to do this week** — exactly one specific, concrete recommended action that follows from the data. Not "monitor closely." Something a person can start doing today. If the data is genuinely unremarkable, say "Steady week — no intervention needed" instead of inventing urgency.
Rules:
- Total length under 250 words.
- Plain language, no jargon, no filler praise.
- If any metric is missing from the input, write "n/a" for it and do not speculate about it.
- Format for Slack: use *bold*, not markdown headers.
User message, built with expressions from the merged item:
Reporting period: {{ $now.minus({days: 7}).toFormat('MMM d') }} – {{ $now.minus({days: 1}).toFormat('MMM d, yyyy') }}
Metrics (this week vs last week):
- Sessions: {{ $json.sessions }} vs {{ $json.sessions_prev }}
- Users: {{ $json.totalUsers }} vs {{ $json.totalUsers_prev }}
- Conversions: {{ $json.conversions }} vs {{ $json.conversions_prev }}
- Revenue (Stripe): ${{ ($json.sum_amount / 100).toFixed(2) }} vs ${{ ($json.sum_amount_prev / 100).toFixed(2) }}
- Transactions: {{ $json.count }} vs {{ $json.count_prev }}
- KPIs from sheet: {{ JSON.stringify($json.kpis) }}
Note the / 100 on Stripe amounts — Stripe returns cents, and “revenue: $482,000” when you made $4,820 is the classic first-run surprise.
Step 7: Deliver it
Slack: add a Slack node — Resource Message, Operation Send, Channel #metrics, Message Text {{ $json.message.content }}. The bot needs chat:write and an /invite into the channel.
Email: add a Gmail node in parallel — Operation Send, recipients your stakeholder list, Subject Weekly Report — {{ $now.toFormat('MMM d') }}, message body the same expression. Tick HTML off; the Slack-style bold reads fine as plain text, or add a small Code node to convert *bold* to <b> if you care.
Activate the workflow. Done — it now runs unattended every Monday.
Free template · n8n
Weekly KPI Report Generator
weekly-report-n8n.json
The template ships with the GA/Sheets/Stripe branches pre-wired and the Sheets branch marked optional. Import it, attach your four credentials, select your GA4 property and spreadsheet, and run Execute Workflow once to test before activating the schedule.
Try it yourself
n8n
One n8n workflow replaces a 45-minute Monday ritual — and the same pattern scales to daily or monthly reports for free.
Start with n8nThe Zapier variant (15 minutes)
If your team already runs on Zapier and your metrics live in one place (usually a Google Sheet that other Zaps populate), the trimmed-down version:
- Trigger — Schedule by Zapier:
Every Week, Monday, 7 a.m. - Google Sheets — Lookup Spreadsheet Row: pull the latest metrics row. Add a second lookup for the prior row if you track weekly rows.
- ChatGPT (or AI by Zapier) — Conversation: paste the same analyst prompt from Step 6 as the system instructions, map the sheet columns into the user message.
- Slack — Send Channel Message and/or Gmail — Send Email: map the AI step’s reply.
That’s a 4–5 step Zap running once a week — about 20 tasks a month, which fits comfortably in Zapier’s cheapest tier (as of mid-2026, check current pricing). The Zapier version trades away the multi-source merge and the dual-period GA pull; if you need those, you’ll either chain more lookup steps or move to n8n. For the broader platform decision, see Make vs Zapier vs n8n.
Common errors and fixes
GA node returns 403: User does not have sufficient permissions. The Google account you authorized doesn’t have at least Viewer access to that GA4 property, or you enabled the wrong API. Enable the Google Analytics Data API (not the legacy Reporting API) in your Google Cloud project, and confirm property access in GA admin.
Stripe returns only 100 charges and your revenue number is low. Get Many caps at 100 per page. Either enable Return All (watch execution time) or switch to Stripe’s reporting endpoint. High-volume shops should pull a pre-aggregated number rather than summing raw charges.
The AI invents numbers or “corrects” your deltas. Almost always a prompt-mapping bug: an expression resolved to undefined, and the model helpfully filled the gap. Check the OpenAI node’s input tab — every metric should show a literal value. The “never recalculate” and “write n/a” rules in the prompt are your guardrails; keep them.
Report arrives at the wrong time. Timezone, every time. Set it in Workflow Settings, not just the trigger, and remember n8n Cloud instances default to the region you picked at signup.
Slack message renders raw asterisks instead of bold. You’re posting with markdown formatting expected, but the Slack node’s Markdown option is off — enable it under the node’s Options.
Zapier: ChatGPT step fails with rate_limit_exceeded on a shared key. AI by Zapier’s built-in allowance is shared and bursty. Connect your own OpenAI API key in the ChatGPT step’s connection settings — it’s cheaper per call and rate limits become yours alone.
Zapier vs Make vs n8n for scheduled reports
This workflow runs once a week, so per-task pricing doesn’t hurt — Zapier is genuinely fine here, and it’s the fastest to set up if your data is already in a sheet. Make sits in the middle: its built-in aggregators handle the Stripe summing nicely and it’s cheaper than Zapier at higher frequencies. Pick n8n when you want multiple data sources merged, dual-period comparisons, or you’ll inevitably extend this to daily standup reports, per-client reports, and anomaly alerts — at which point execution-based pricing and the Merge/Summarize nodes pay for the slightly steeper learning curve.
Where to go next
The same skeleton — schedule, fetch, summarize, deliver — covers most recurring AI reporting. Two natural extensions: feed meeting outcomes into the report by combining it with meeting notes to tasks, and if your KPI sheet is populated by hand today, automate the upstream data entry with the patterns from lead capture to CRM so the Monday report sits on top of data nobody had to type.