A Cloudflare Worker pulls every source on a schedule and writes one normalized payload. A password-gated dashboard reads it. Everything below the first card only works because that layer exists: you cannot ask a question across the business until the business is in one shape.
🔌
12 sources, one payload
Unified Data Layer
A scheduled Worker ingests payments, bookings and availability, all three ad platforms, call and SMS records, Google reviews, session and channel analytics, event inquiries from the inbox, organic social, transactional email and local weather. Sources without a usable API are bridged by in-account scripts that push into the same pipeline. Everything lands normalized in KV, so every surface downstream reads one shape instead of twelve.
Stack: Cloudflare Workers, KV, cron triggers, Google Apps Script bridges for analytics, ads and inbox.
💬
Plain English, live data
Ask Your Ops
A question box that answers from the live operating payload rather than from training data. It carries prior turns, so a follow-up like "is that today or the total?" resolves against what was just asked instead of latching onto the first number it sees. Strictly read-only: it reports on the business, it never changes anything in it.
Stack: Workers AI running Llama 3.3 70B, grounded on the live payload, multi-turn context, read-only by construction.
📋
Written on every refresh
Automatic Ops Brief
The dashboard does not wait to be interrogated. On every scheduled refresh the Worker writes a short brief in plain language covering where the week stands, what moved and what needs attention, and serves it at the top of the dashboard. Opening the tab is enough. Nobody has to know which panel to check first.
Stack: Generated on the cron run, cached in KV, served read-only to the gated dashboard.
📊
Google + Meta + TikTok
Blended Ad ROI and Attribution
All three ad platforms normalized to a common shape and rendered as one blended view plus a per-channel breakdown, so the budget conversation happens against comparable numbers. Alongside it, a booking-source panel lays online purchases by acquisition channel next to the walk-in and same-day split, because attribution that ignores the guests who never touched a campaign flatters every campaign.
Stack: In-account ads script to Worker for one channel, normalized ingest for the others, GA4 channel feed on a six-hour cycle.
🔮
Forecast + holdout test
Demand Forecast and Incrementality
A model built on the things that actually move a venue day: day of week, local events, school and holiday calendars, and weather. It drives a staffing tier so labor is planned against expected demand rather than last week's feeling, and it was recalibrated against logged trading days, which cut walk-forward error by roughly a third. The same model powers an incrementality holdout: when spend is paused, the question is not whether bookings fell but whether they fell below what the model expected anyway.
Stack: Pure-function forecast model shared by the dashboard and the staffing view, walk-forward calibration, holdout comparison against expected demand.
🧩
One person, many records
First-Party Contact Graph
The same guest arrives as a booking, a payment, a phone number, an inbox enquiry and a widget conversation, spelled differently each time. A contact graph resolves them into one record, so a name on the board carries everything that person has ever done with the venue rather than whichever fragment the tool in front of you happens to hold.
Stack: Identity resolution in the Worker across booking, payment, phone and inbox records, served password-gated to the dashboard.
🔐
Enforced before assets
Roles and Shared State
Authentication runs in middleware ahead of any asset being served, so no financial figure reaches a browser unauthenticated. Beyond a single password, roles decide which lanes and panels a given person sees, and the parts of the board the team edits by hand are shared state, so what one owner marks as done is what the next one opens.
Stack: Pages Functions middleware with HMAC session cookie, role-scoped API responses, shared editable state in KV.
📆
Kept, not just displayed
Daily History
Vendor dashboards show you a window and forget it. Every trading day is written down here with the weather and the calendar it happened under, which is what makes the forecast trainable and the holdout test possible at all. It also means a question about a Tuesday in July has an answer, rather than depending on whether someone screenshotted it at the time.
Stack: Daily sales, weather and calendar history written by the refresh Worker to KV, served to the dashboard and read by the forecast calibration.