Work · Reports & Insights Portal · ThinkingAI, 2026

Marketing BI we own instead of rent

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.

6 sourceswired through their own APIs
11+ reportslive on the portal
No subscriptionthe aggregator is owned
Before

The CEO asked for one place to see the numbers

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.

  • Every number in a different platform. HubSpot, GA4, Search Console, Ahrefs, LinkedIn, Google Ads. Each with its own login, its own dashboard, no single view.
  • Aggregating meant paying for it. A subscription or a BI team stitching connectors. Cost and dependency for what is now an API call.
  • Reports were stale and human-only. Point-in-time exports a person made, readable by a person. Nothing an agent could scan to reason over.
The system

One portal, five parts

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.

Part 01 · The sources

Six sources, each pulled through its own API

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 MCP
Data sourceseach pulled through its own API/MCP6 wired
HubSpotfunnel · deals · contacts
GA4 + BigQuerypage journeys · creds pending
GSC · Ahrefssearch + SEO
Zernio · Google AdsLinkedIn + SEM
Part 02 · The hub

Role-modular reports

Reports 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+ total
Hub indextabbed by audience, one card per report3 tabs
Marketingfunnel · pages · LinkedIn · SEO
Salespipeline dashboard
Personalweekly to-do board
Part 03 · The refresh

Why an agent does not run the daily update

The 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 fragile
Refresh designwhy cron, and what runs on itdaily cron
Agent on a schedulecontext limits + token cost
Cheaper sub-agentneeds daily monitoring
GitHub Actions cron → build → Vercel deploy
hubspot-funneldaily · auto-dated
page / linkedin perfdaily · auto-dated
sales · ai4 · otherson demand
Part 04 · Two readers

Built for humans and agents

Two 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 Reporting
Two consumerssame reports, different readersdual
Humanscans the boards
Agent, on requestanswers a question
Agent, on a schedulefinds anomalies unwatched
Bridge→ agent-native reporting
Part 05 · The gate

Encrypted, gated and self-serve

Every 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 webhook
Gate + request flowencrypted at rest, self-serve requestsAES-256-GCM
Buildencrypt with PBKDF2 key
Deploydecryptor + ciphertext only
ViewWeb Crypto decrypts in browser
Requesthub form → serverless → Lark
What it changed

Marketing became the most legible team in the company

The 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 agent
What the team getsthe part that outlived the dashboardthe payoff
One place for every marketing number
Drill-down under every summary
Daily refresh with no human step
Cross-team visibility, on the record
Agent-readable for scheduled monitoring
Decisions I'd make again

Calls I made on purpose

01 Own the aggregator

Every 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.

02 Modular by role

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.

03 Freshness must be visible

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.

04 Encrypt at rest, decrypt in the browser

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.

05 Requests go to the portal

The hub's Lark-wired form takes report requests, so asks land in a queue instead of my DMs.

06 One dataset, two readers

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.

Status

What's real today

The portal runs, a defined set refreshes itself daily, and the whole layer is ours.

The limits

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.

Reusability

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.

The pieces · honest status
Portal · hub · gate · request formRUNNING
Daily cron · defined report setRUNNING
GA4 / BigQuery page-performanceBLOCKED ON IT
Agent-insight layerEMERGING
GitHub Actions Vercel HubSpot API GA4 + BigQuery Search Console Ahrefs Google Ads
He is a professional who brings his capability to learn things quickly to get work done successfully. His professional ethic and ease to adapt to any team make him a good element to any organization.
Bruce Au Bruce AuGeneral Manager
PRO
More work

Want marketing BI you own instead of rent?

This portal exists so a way of working can run on top of it. Agents read these reports and hand back findings.