What you'll build
Agency owners lose four to eight hours a week building client reports by hand. A weekend with Claude Code replaces that with a script that pulls data from your platforms, renders a branded HTML email or PDF, and delivers to every client every Monday morning. You write a config file per client, run the script once to verify, then never touch the report again - the schedule handles it. This is one of the highest-return automations an agency can build with Claude Code.
What you're building
You're building a script that runs every Monday morning, pulls last week's data from the tools your clients care about, formats it into a branded PDF or HTML email, and sends it to each client. No more screenshotting Google Analytics on Sunday night. No more copy-pasting numbers into a Notion doc. The script reads a clients.json file that tells it who gets what, runs once a week on a schedule, and logs the result so you can prove the report went out when the client asks. The deliverable looks at least as good as the one you make by hand, and arrives more reliably.
This is one of the highest-return automations an agency can build. Three clients times an hour of report work each is fifteen hours a month back. At a hundred and fifty dollars an hour, that's twenty-seven hundred dollars of recovered time. The build itself is small enough to fit in a Saturday and a Sunday. And once it works for three clients, it works for thirty with almost no extra code, because each new client is a config file rather than new logic.
What you need before you start
You don't need to be a developer to follow this, but you do need to be comfortable on the command line. You need a laptop with Node installed, a code editor, and API access to whatever tools you're reporting on. Most agencies need Google Analytics, Google Search Console, Meta Ads, and maybe a CRM. Each one has an API, and Claude Code knows how to talk to all of them. The auth setup for Google's APIs is the most tedious step, so budget an hour on Saturday morning for OAuth consent screens, service accounts, and scope approvals.
- Node 20 or later and a code editor like Cursor or VS Code
- Claude Code installed and authenticated
- Read-only API tokens for the platforms you report on
- A Resend, Postmark, or SendGrid account for sending email
- A logo and one brand color per client
- A sample report you've sent recently as the template
Saturday morning: the data layer
Start by asking Claude Code to scaffold a Node project with TypeScript, dotenv, and an index.ts entry point. Then build the data fetchers one at a time. For each platform, create a file under src/sources, give Claude the API docs URL and your last week's date range, and let it write the fetch function. The function should return a plain object with the metrics you care about. Don't over-engineer the shape. A flat object with five to ten fields per source is plenty. You can always nest later, but flat is easier to render and easier to debug.
Test each source by itself before moving on. Run it from the command line, see the JSON, confirm the numbers match what you see in the dashboard. If they don't match, fix the time zone. Time zones are where ninety percent of report bugs live. Set every API call to your client's local time zone, not UTC, and write it down in the config. A client in Sydney reading a report that ends at midnight UTC is reading data that ended at 10am their time, which is wrong in the way that erodes trust quietly.
Save every raw API response to a data/raw/{client}/{date}/{source}.json file before any transformation. Disk is cheap and reruns are free. When the report looks weird three weeks from now and you want to know whether the source data changed or the rendering logic changed, you'll have the receipts.
Saturday afternoon: the template
Build the template as a function of the data, not the data as a function of the template. The render function takes the typed metrics object and returns HTML. When a new metric matters, you add it to the object first, then to the template. This separation keeps the template clean and means you can swap the layout without touching the fetch logic.
Pick one of two paths. Path A is HTML email with inline CSS, which renders inside Gmail and Outlook and is enough for most clients. Path B is a PDF generated with Playwright or Puppeteer, which looks more like a deliverable and is what bigger clients expect. Path A is faster. Path B is more impressive. You can build A this weekend and add B next month.
Ask Claude to write a render function that takes the client config and the fetched data and returns a string of HTML. Include a header with the client's logo, a summary section with three to five headline numbers, a section per platform with a small chart, and a footer with your agency branding. For charts, use QuickChart.io, which returns a PNG from a URL and works in both email and PDF. Skip interactive charts. Email clients block JavaScript, and clients open reports on their phones nine times out of ten.
Keep the design minimal. White background, dark text, one accent color for headers and the chart bars. Resist gradients, drop shadows, and round-cornered cards stacked on round-cornered cards. The report is a document, not a dashboard, and it should read like a one-page memo from someone who respects the client's time.
Choices to make along the way
Resend versus Postmark versus SendGrid: Resend is the easiest to set up, has a React-friendly API, and works fine for the volumes an agency sends. Postmark is the most reliable for transactional mail and has better deliverability if you're worried about spam folders. SendGrid is the legacy choice and has more knobs than you need.
Cron on your laptop versus GitHub Actions versus a tiny server: laptop cron is fine while you have one or two clients but breaks the first time you go on vacation. GitHub Actions on a schedule is free, runs in the cloud, and is the right answer for ten or fewer clients. A tiny Hetzner or Fly server with node-cron is the right answer above that. Start with GitHub Actions.
Sunday morning: client configs and personalization
Create a clients.json or, better, a clients/ folder with one file per client. Each file holds the client name, logo URL, brand color, the platforms to include, the recipient emails, and any custom commentary you want at the top of the report. Have Claude Code read this folder on each run, loop through every client, fetch their data, render their report, and send it. One script handles all of them, and onboarding a new client is a five-minute job of copying an existing config and editing five lines.
How to test it
Send the first three runs to yourself, not to clients. Spot check every number against the source dashboards. Send the next three runs to clients with a manual subject-line warning that the report is new. Ask for feedback on what's missing and what's confusing. Adjust. Once a client tells you the new report is better than the old one, you're done testing that client and you can move to set-and-forget. A good signal you're done: the client forwards the report to someone else with a one-line note instead of replying with questions.
How to ship it
Push the repo to a private GitHub. Add a workflow file at .github/workflows/weekly-report.yml that runs on a Monday morning cron. Set all your API tokens as repo secrets, not environment files. Add a manual workflow_dispatch trigger too, so you can run the report on demand when a client asks. The whole pipeline costs zero dollars a month at agency volume. The club at claudecodeclub.ai has a working starter template and a Slack channel of other agency owners running the same playbook, which is the fastest way to skip the early mistakes.
Set the cron to run at six in the morning client time, not yours, and stagger clients in different time zones by an hour each to avoid hitting API rate limits all at once. Add a heartbeat: if the workflow doesn't complete by eight, you get a Slack ping. Silent cron failure is the worst class of failure because the report just doesn't go out and nobody notices until the client emails on Tuesday.
Track delivery status. The script should write a row to a deliveries log every Monday with the client, the timestamp, the email message id from Resend, and the status. When a client says they didn't get the report, you can answer with the exact send time and the bounce reason. This builds trust faster than the report itself.
How to extend it
After v1, add a comparison to the previous week so the report shows deltas in green and red. Add an AI-generated insight at the top of each section, written by Claude from the raw numbers. Add a dashboard at reports.youragency.com where clients can see historical reports on demand. Add a CSV export. Each of those is a half-day project, not a week. The big one to ship eventually is a client-facing dashboard where the same data renders live, because it shifts the support conversation from 'where's my report' to 'I checked the dashboard, here's my question.'
Once the pipeline is solid, productize it for other agency owners. The same script with a config-per-client UI becomes a paid tool you can sell for fifty dollars a month to other agencies, and it's a category Mercury-sized agencies happily buy. The build sits naturally between full-blown reporting platforms and a manual spreadsheet, which is a sweet spot most current tools miss.
Common gotchas
Time zones are the first gotcha and the second gotcha. API rate limits are the third, especially Meta Ads, which is stingy and slow. Always cache the raw fetch results to a JSON file before rendering, so a render bug doesn't force you to re-fetch and burn quota. The last gotcha is silently failing. If a source returns zero rows, the script should email you, not the client. A blank report is worse than no report.
Token refresh on Google APIs is the gotcha you only meet on week three. Access tokens expire after an hour, refresh tokens expire after six months of inactivity, and the script that worked perfectly for a quarter suddenly fails one Monday morning. Build a token refresh helper from day one and store refresh tokens in a place that's easy to rotate, not hardcoded in env vars you have to track down.
Email rendering is the gotcha that bites every time you add a new chart. Outlook renders HTML differently from every other client, and CSS Grid is unsupported there. Stick to tables for layout in the email template, even though it feels like 2005. Use a service like Litmus or PutsMail to preview across clients before shipping a new template, and screenshot the result in three viewports as a sanity check.
How Claude Code writes the hardest parts for you
The parts of this build that stump most agency owners are the Google OAuth flow, the Meta Ads API pagination, and the HTML-to-PDF rendering pipeline. These are well-documented but tedious to assemble. Claude Code handles all three. You paste the relevant API docs URL or describe what you want, and it produces a working TypeScript module - the OAuth refresh helper, the paginated Meta fetch, the Playwright PDF render - that you can inspect, tweak, and run. The skill is knowing which module to ask for next, not writing the module from scratch.
Ask Claude Code to 'write a src/sources/googleAnalytics.ts module that fetches sessions, users, and conversions for a given date range using the GA4 Data API, authenticated via a service account JSON file, and returns a typed MetricsObject.' That single prompt produces the API call, the TypeScript type, the date-range parameters, and a note about which scopes the service account needs. Compare that to reading the GA4 API reference yourself and assembling the same module by hand - two hours versus fifteen minutes. The time difference across eight or ten sources is where the weekend savings come from.
The same applies to the render layer. Tell Claude Code 'write a renderReport function that takes a ClientConfig and a MetricsData object and returns an HTML string with a QuickChart bar chart, using table-based layout for Outlook compatibility.' It produces working code that you refine against real email client previews. The first draft will need adjustments - spacing, color hex codes, the chart URL format - but the structure is correct and the Outlook-safe table pattern is in from the start.
Building a delivery log and staying accountable
The difference between a professional automated report and a hobbyist one is the paper trail. Every Monday run should write a row to a deliveries.jsonl file with the client name, the run timestamp, the number of records fetched per source, the email message ID from your sending provider, and the delivery status returned by the API. Append-only JSONL is the right format because it diffs clearly in git and never requires a schema migration.
Add a weekly summary email to yourself that arrives Tuesday morning listing every client, their delivery status, and any sources that returned zero records or threw errors. This is a two-minute addition with Claude Code - a second script that reads deliveries.jsonl and sends a Markdown summary via Resend - and it converts you from someone who hopes the automation worked into someone who knows it worked. Clients expect the reports on Monday. You want to know by Tuesday if any failed, not when a client emails on Wednesday.
The club at claudecodeclub.ai has agency owners who use this exact pipeline for ten to forty clients. The consensus pattern is GitHub Actions for the schedule (free, reliable, auditable), Resend for email delivery (simple API, generous free tier), QuickChart for charts (URL-based, no server-side rendering dependency), and a private repo with one branch per major client-config change. The $9/month membership includes the full starter template and a Slack channel where you can share the config schema and get feedback from other agency owners before you run it on real clients.
Common questions
Do I need to be a developer to build this?
No, but you need to be comfortable on the command line. Claude Code writes the actual code. You give it the API docs links, run the script, and check that the numbers match the source dashboards.
HTML email or PDF report?
Start with HTML email using inline CSS. It renders inside Gmail and Outlook, it's faster to build, and most clients are happy with it. Add PDF generation via Playwright next month if a bigger client expects a deliverable.
Where should the script run?
GitHub Actions on a Monday morning cron is free and right for up to ten clients. Above that, move to a tiny Hetzner or Fly server with node-cron. Don't run it on your laptop unless you never go on vacation.
How do I avoid time-zone bugs?
Set every API call to the client's local time zone, not UTC, and write the zone into the client config file. Time zones are where most report bugs live, and being explicit per client kills the whole class of bug.
What email service should I use?
Resend is the easiest setup and works fine for agency volumes. Postmark has better deliverability if you're worried about spam folders. SendGrid is the legacy choice and has more configuration than you need.
What if a data source returns nothing?
The script should email you, not the client. A blank report goes out as a 'something broke' alert to your inbox, with the raw fetch results attached so you can debug without re-running.
More to build
Build it. Ship it. Get paid.
Step-by-step lessons for every one of these inside the club. Join Claude Code Club for $9/month.
Related: the library, guides, and comparisons.
