Every platform we use now ships an API or an MCP. So instead of paying a Funnel.io-type aggregator, I pulled each source myself, built each into a role-modular report, and put them all on one encrypted portal that refreshes itself.
The ask was specific and reasonable: a centralized dashboard covering website, sales, marketing and some operational reporting, in one place. The numbers all existed. They lived in six different logins, and nobody saw them side by side.
I evaluated the two normal answers before building anything. HubSpot was first, and it broke on seats and scope: I hold a marketing core seat, covering the sales side properly means buying more licenses, the non-HubSpot platforms do not centralize into it cleanly, and customizing the reports past a point gets hard. Funnel.io was the second look and it is a decent product, but it is another third-party platform to depend on and pay for, and it still left the everything-in-one-place problem unsolved. So I built it, as a Vercel site with each report wired to its own source.
The portal is a static site assembled from platform APIs, rebuilt by a cron job, encrypted before it ships, and read by two kinds of consumers: people and agents.
Each source is pulled through its own API or MCP, because every one of these platforms now exposes one. Six are wired today: HubSpot, GA4 with BigQuery export, Google Search Console, Ahrefs, LinkedIn via Zernio, and Google Ads. That set is the aggregator, and I own it.
HubSpot via REST Service Key + MCP · GA4 Data API + BigQuery export · Ahrefs via MCPReports are split by audience, so nobody scrolls past someone else's numbers. The hub has three tabs (Marketing, Sales, Personal), and each report is a self-contained card in the right tab. The design intent: each role gets its own set.
Live cards: weekly-todo · hubspot-funnel · page-performance · linkedin-performance · sales · competitor-content · sem-engine + more · 11+ totalThe obvious design is to let an agent rebuild the reports on a schedule. I tried to reason it through and it fails on two counts. These pulls carry a lot of rows, so a daily agent run keeps hitting context limits and burns expensive tokens to re-derive the same tables. Push it down to a cheaper model and the cost improves while a new problem appears: something has to check every morning that it actually ran and that the output is sane. Now the automation has a daily babysitter, which is the thing I was trying to remove.
So the refresh is deliberately boring. GitHub Actions runs the job on a cron, pulls the fresh data, rebuilds the report, and tells Vercel to deploy the result. Deterministic work stays deterministic and costs almost nothing. Each auto-dated card syncs its “last updated” to the real pulled date, so staleness is visible instead of implied. Adding a stakeholder's report means updating a template once; after that it maintains itself and I never look at it again.
The honest split: the HubSpot funnel and the page- and LinkedIn-performance cards run on the daily cron; other reports are agent-refreshed on demand. I'm not claiming every card auto-updates daily.
Judgment goes to the models, scheduled retrieval goes to a cron. Mixing the two is what makes agent automation expensive and fragileTwo consumers by design. A manager reads the boards to see performance. An agent reads the same structured data to do what a static dashboard can't: scan across the numbers, spot the problem, pull an insight. That second reader is the bridge into the agent-native operating model.
That is what the portal actually buys a manager. Their agent can be pointed at these reports whenever they want an answer, and it can also run on a schedule while nobody is looking, reading the day's numbers and surfacing anomalies rather than waiting for someone to notice a chart. I run mine the same way. Nobody has to open a dashboard for the dashboard to be doing work.
Same centralized data, two readers · this portal is the delivery surface for Agent-Native ReportingEvery report is AES-256-GCM encrypted with a PBKDF2-derived key at build time; the deployed file is a decryptor plus ciphertext, so viewing source without the password shows nothing. Decryption happens client-side via Web Crypto. The hub also carries a marketing-request form that posts through a Vercel serverless function to a Lark group bot, so the team asks for reports inside the portal.
build-public.js encrypts · api/request.js → Lark webhookThe reporting value is real, and the visibility turned out to matter more. Everything marketing runs is now published in one place, on a daily cadence, at a level of detail anyone can drill into. Another team no longer has to ask what we are working on or take our word for how a channel is performing, and neither do I when I need to answer for it. That is a fairly exposing way to operate, and it is the point: numbers that update themselves are harder to present selectively.
The combination is what I had not had before. One place to look, drill-down underneath it, a refresh that needs no human, and an agent that reads the same data on its own schedule. Anyone in the company can now check the reporting directly; nobody assembles it.
Centralized, drilled down, self-updating, and readable by both a person and an agentEvery platform in our stack now has an API or MCP. That makes the paid-aggregator category optional. I chose to assemble the layer myself and keep it.
A sales lead should open the portal and see pipeline; a marketer should see funnel and pages. Three tabs, per-role card sets. Relevance beats completeness.
A dashboard with a vague date is a trap. The auto-dated cards sync “last updated” to the actual data-pull date, so a stale card looks stale.
Customer data never sits readable in the deployed source. AES-256-GCM with a PBKDF2 key, decrypted client-side by Web Crypto after the password.
The hub's Lark-wired form takes report requests, so asks land in a queue instead of my DMs.
I structured the reports so an agent can reason over the same data a manager scans. That single decision is what connects this system to the operating model it serves.
The portal runs, a defined set refreshes itself daily, and the whole layer is ours.
I'm not claiming every card auto-updates daily, and this is no polished commercial BI product or finished insight-to-execution loop. I also publish no cost-comparison number, because I haven't measured one.
The unit transfers: source API → generator → encrypted static build → Vercel + a daily cron → a tabbed, role-modular hub. Any team whose tools expose APIs can own this layer instead of renting it.
GitHub Actions
HubSpot API
GA4 + BigQuery
Search Console
Google Ads
This portal exists so a way of working can run on top of it. Agents read these reports and hand back findings.