← Back to dashboard
Help

How Your Dashboard Numbers Work

A plain-language guide to where every metric comes from, how it gets to your screen, and what math happens along the way.

The Big Picture

Data moves left to right — from your platforms, through a data pipeline, into your dashboard.

Your Platforms
🛒 Shopify (revenue, orders)
📊 Shopify GraphQL (sessions, units)
🔄 Recharge (subscriptions)
📊 Google Ads
📱 Meta Ads
📧 Klaviyo (email revenue)
🦁 LoyaltyLion (loyalty revenue)
📷 Instagram
● = live   All 8 sources active. GA4 removed — sessions now via Shopify directly.
Data Pipeline
⚡ Windsor.ai
Handles Shopify revenue/orders, Google Ads, Meta Ads, Instagram. Our pipeline pulls from Windsor once a day and on demand.
🔗 Direct APIs (4 sources)
Shopify GraphQL (sessions + units), Recharge (subscriptions), Klaviyo (email revenue), LoyaltyLion (loyalty revenue). These connect directly for better accuracy.
All connections are read-only. The dashboard never writes back to your platforms.
Database
🗄️ Supabase
Stores weekly snapshots of all your KPIs. Each week gets one row with every metric.
📅 Fiscal Calendar
Oct 1 – Sep 30 fiscal year. Weeks run Sunday through Saturday.
Historical data is locked in. Current week updates live when you load the dashboard.
Your Dashboard
🏠 Home Overview
📢 Paid Media
📧 Email & Flows
🔄 Subscriptions
🤝 Acquisition & Retention
🎯 Goals
Protected by username/password login. Auto-deploys from code updates.

How Your Data Refreshes Under the hood →

1

Platforms Update

Shopify processes an order, Google records a click, Klaviyo sends an email — activity happens in real time inside your tools.

2

Daily Sync, 2 AM ET

Once a day, our pipeline pulls fresh numbers from all eight platforms and writes them into the database.

3

Auto-Refresh When You Open

A cron runs every 6 hours to keep data fresh. If you land on the dashboard and the last refresh is still more than 6 hours old (missed cron, etc.), a fresh pull kicks off in the background. The Refresh Now button triggers both the weekly snapshot and the daily Shopify chart on demand.

4

Week Gets Locked In

When a fiscal week closes, the numbers for that week stop moving. The rolling renewal rate locks in Sunday 00:01 ET.

How Pacing and Colors Work

Every KPI card, goal panel, and metric on the dashboard uses one rule: your result is compared to how far through the period you are, and that drives whether the number shows green, orange, or red.

The Color Rule (applies everywhere) Under the hood →

G

Green — on track

Your actual is at or above 95% of the paced target. You're keeping up with (or ahead of) where you need to be for this point in the period.

O

Orange — watch

You're between 75% and 95% of the paced target. Not an emergency, but worth a look — one more soft day and this becomes a problem.

R

Red — behind

You're below 75% of the paced target. You're meaningfully off pace and need to either change something or have a clear reason why.

Where Every Number Comes From

Each card explains the source, the calculation, and any caveats for the metric.

Click any card to expand. Tap the chevron or anywhere on the row.

Shopify

Net Revenue

Top-line sales after discounts, before shipping/tax

This is your top-line sales number, after discounts and before shipping/tax. It's the "real" revenue figure.

Source: Shopify via Windsor
Field: order_net_sales
How: We sum every order's net sales (gross minus discounts minus returns) for the fiscal week. Exact match to Shopify Analytics "Net Sales." SUM(order_net_sales) per week
Shopify

Total Orders

Distinct order count for the period

The total number of orders placed in a given week.

Source: Shopify via Windsor
How: We count each unique order in the week. Includes one-time purchases, subscriptions, and reorders. COUNT(DISTINCT order_id) per week
Calculated

Average Order Value (AOV)

Revenue ÷ orders. Small AOV moves swing revenue hard.

How much a customer spends per order, on average. A key lever — small AOV increases have outsized impact on revenue.

Source: Derived from Shopify data
How: Net revenue divided by total orders. Simple division, same week. AOV = Net Revenue / Total Orders
Shopify

New Customers

First-order shoppers per week, time-invariant

How many brand-new shoppers placed their first-ever order during the week. This is your top-of-funnel acquisition number — it pairs with CPA to tell you what you're paying to win each one.

Source: Shopify Admin GraphQL API (direct, not Windsor)
Definition: A customer is "new" in fiscal week W if their customer.createdAt (in store timezone, America/Los_Angeles) falls in W AND they placed at least one order in W. Shopify creates a customer record the first time someone checks out, so createdAt is effectively "first-order date." This definition is time-invariant — historical weeks won't shift as customers come back.

Why not Windsor's flag: Windsor's order_customer_number_of_orders = 1 uses the customer's current lifetime order count. When a customer comes back and orders again, their Nov purchase retroactively stops being "new." Historical weeks decayed 22–33% under the old method; this one holds steady.

Variance vs your spreadsheet: All 28 re-backfilled weeks land within 3–6% of your marketing sheet. The residual gap is likely Shopify Admin's inclusion of admin-created customers or refund-edge orders. COUNT(DISTINCT customer.id) WHERE customer.createdAt ∈ week AND order.createdAt ∈ week
Shopify

LTV (1-yr cohort)

Per-customer revenue at offsets 0–51 weeks. Powers /acquisition.

Lifetime value of customers who made their first order in a given week, tracked weekly out to 52 weeks. Replaces CPA as the headline acquisition metric — leadership cares about LTV, which measures long-term customer value more accurately than cost-per-acquisition.

Source: Shopify Admin GraphQL — two-stage cohort sweep
How: Stage 1 finds every customer whose first order is in the cohort week (in store TZ). Stage 2 sweeps 52 weeks of subsequent orders for those customers. Per-cohort series stored in weekly_snapshots.ltv_1yr_series (jsonb, 52 offsets).

Cohort maturity: a row is "mature" when 52 weeks have elapsed since the cohort end. Immature cohorts re-run every Sunday on Hetzner (00:31 ET) until matured; mature cohorts never re-query Shopify. Why Hetzner: 52-week sweep takes 3–8 minutes per dense cohort, exceeding Vercel's ~5-min edge TTFB cap. LTV(N) = Σ revenue per cohort customer through week N − cohort_start
Google Ads

Google Ads Spend & ROAS

Daily spend + return aggregated weekly

How much you're spending on Google and what you're getting back. ROAS = "for every $1 spent, you got $X back."

Source: Google Ads via Windsor
Fields: spend, conversions, roas
How: Windsor pulls daily spend and conversion data. We sum spend and average ROAS across the week. ROAS = Conversion Value / Spend
Meta Ads

Meta Ads Spend & ROAS

Same as Google — for Facebook + Instagram campaigns

Same concept as Google — spend versus return, but for Facebook and Instagram ad campaigns.

Source: Meta Ads via Windsor
Fields: spend, impressions, clicks, conversions
How: Daily aggregates summed per week. ROAS is conversion value divided by spend. Sprint 3.19 added ad-level breakdowns (creative grid, audience overlap, fatigue scoring) on /paid-media. ROAS = Conversion Value / Spend
Calculated

Combined Spend & CPA

Total ad spend + cost per acquired customer

Your total ad spend across all channels, and how much it costs to acquire one customer. CPA is kept for operational context but LTV (1-yr) is the headline acquisition metric on /acquisition.

Source: Derived from Google + Meta
How: These are auto-calculated columns in the database — we never set them manually. Combined Spend = Google Spend + Meta Spend CPA = Combined Spend / New Customers
Klaviyo

Email Revenue (Campaign + Flow)

Klaviyo-attributed conversion value, sum of campaigns + flows

Revenue Klaviyo attributes to your campaigns and flows. Matches the number on Klaviyo's Business Performance Summary dashboard — the same one you pull on Monday mornings.

Source: Klaviyo direct API (not Windsor)
How: We call Klaviyo's Campaign Values Report and Flow Values Report APIs. Each returns per-campaign / per-flow conversion_value for the date range; we sum both for the total.

Per-flow per-message detail (Sprint 3.20): The /flows page now shows per-flow per-email-position metrics from weekly_flow_messages — funnel decay, RPR by position, subject lines, suggested actions per flow type. Email Revenue = sum(Campaign.conversion_value) + sum(Flow.conversion_value)
Shopify

Sessions (Online Store, Human-Only)

Bot-filtered visitor sessions via ShopifyQL

How many real human visitors came to your online store. We explicitly filter out Shopify-classified bot traffic — which matters a lot during November–January, when bot waves can otherwise balloon raw session counts by 10–15× normal.

Source: Shopify ShopifyQL API (v2026-01)
How: We query Shopify's analytics database directly using ShopifyQL, with a WHERE clause that keeps only sessions Shopify labeled as human. Online store sessions only (not POS or mobile app). Bot classification launched Oct 7, 2025, so FY2026 W1 (Oct 1–4) has no classification and returns empty. Shopify reclassifies sessions asynchronously 24–48 hours after they occur, so very recent numbers may still settle. FROM sessions SHOW sessions WHERE human_or_bot_session = 'human' SINCE [start] UNTIL [end]
Recharge

Subscription Revenue (Subscribe & Save)

Recurring orders, net of refunds — direct Recharge API

Revenue from recurring subscription orders, net of refunds.

Source: Recharge direct API
How: We pull all subscription orders for the week directly from Recharge and subtract refunds. All weeks use the direct API path (zero Windsor fallback after Sprint 10.12). Sub Revenue = SUM(order_total) − SUM(refunds)
LoyaltyLion

Loyalty Revenue, Tiers & Redemptions

Loyalty-attributed revenue + Seeker / Health Empowered / Optimizer tiers

Revenue from orders placed through your loyalty/rewards program, points earned/spent each week, and the tier breakdown across your ~182K enrolled members.

Source: LoyaltyLion direct API + Shopify LL-* discount join
Attributed revenue: Paginated /v2/orders (skip cancelled): total_paid − total_refunded.
Points + redeemers (Sprint 3.13): /v2/activities rolled up — earned (positive approved values), spent (|negative|), distinct redeemers per week.
USD discount spend + rewards-by-value: Shopify Admin GraphQL filters discount_code:LL-*. Shopify is the ground truth for actual USD redeemed (LoyaltyLion has no /v2/rewards or /v2/tiers REST endpoint).
Tier breakdown: Full /v2/customers scan on Hetzner Sun 01:01 ET (~19 min wall-clock). Loyalty Rev = SUM(total_paid) − SUM(total_refunded)
Recharge

Subscription Health: Active, New, Cancelled, Renewal Rate

Active count, weekly net movement, customer-level renewal — target ~60%

The full picture of subscription health — how many active subs you have, how many new ones came in, how many cancelled, and what percentage of customers stay.

Source: Recharge direct API
Active subs: Count of all currently-active subscriptions, adjusted for historical accuracy by checking subs that were active at week-end but cancelled since.
New subs: Subscriptions created within the week. Same-day cancel/reactivate not counted (matching Recharge dashboard).
Cancelled: Subscriptions cancelled during the week, minus those recovered by a successful charge. Same-day create-and-cancel excluded.
Renewal rate: Customer-level metric. For each cancelled customer, we check if they have zero remaining active subscriptions. Renewal rate = 1 minus that churn percentage. Validated 62.6% vs your Recharge dashboard 2026-04-21. Computed weekly on Hetzner (Sun 00:01 ET cron) so the 15–20-min full sweep doesn't hit Vercel's edge TTFB cap.

Subscription cohorts (Sprint 3.17): /subscriptions shows retention curves (d30/60/90/180/365 by acquisition month), first-90 cohort analysis with paid-share + outlier flags, and quarterly LTV-by-cohort heatmap. All compute on Hetzner Sun 01:31 ET.
Shopify

Units Sold (Bundle-Expanded)

3-Pack=3, 6-Pack=6 — Shopify order date (NetSuite uses ship date)

Total product units sold in a week, with multi-pack products counted as individual units. A "3-Pack" counts as 3 units, a "6-Pack" as 6. Counted by Shopify order date — when the customer placed the order.

Source: Shopify Admin GraphQL API
How: Pulls every order's line items from Shopify. If a variant title matches "3-Pack" / "6-Pack" / etc., quantity is multiplied by the pack size. Refunded items are subtracted (also pack-expanded). Units = SUM(qty × pack_size) − SUM(refunded_qty × pack_size)
Reconciliation with NetSuite. The dashboard counts by order date (what customers bought that week); NetSuite's "Sales by Item Summary" counts by ship date (when revenue is recognized in the ERP). Any given week the two views differ by ~10–15% because orders placed Fri–Sat ship Mon–Tue and land in the next NetSuite week. The gap converges to under 1% over quarterly and YTD periods. Both numbers are correct — they answer different questions. Validated line-by-line for W29 on 2026-04-21.
Shopify

Kit Mix, Units per Order, Category Roll-up, Top Products

Per-SKU breakdown + configurable category taxonomy

Breakdown of each week's units by SKU: 3-pack / 6-pack / single share of total units, average items per order, and the top products by units sold. Includes a configurable category taxonomy plus a Flagship cross-cut tag covering curated bestsellers.

Source: Shopify Admin GraphQL API (per-SKU)
How: Every line item in every order is grouped by SKU and written to weekly_product_breakdown in Supabase. Refreshed every 6 hours by the per-SKU cron, or manually via Refresh Now. Kit % and Units per Order are computed client-side from the raw per-SKU rows so multi-week rollups (MTD / QTD / YTD) compose correctly.

Pacing-aware deltas (Sprint 3.32): Category cards on /units compare partial-week WTD totals to paced prior-week totals (priorTotal × paceFactor) — so Tuesday-morning reads stop showing phantom 45% drops.

CSV exports: Two dropdowns on /units/products — Weekly (last 3 fiscal weeks) + Monthly (last 3 calendar months) — honor the active category/flagship filter. Kit % = SUM(net_units where pack_size=N) ÷ SUM(net_units)
Units per Order = SUM(net_units) ÷ SUM(distinct orders)
Reports

Weekly / Monthly / Quarterly / Year-End Reports

3-stage flow with AI commentary, PDF export, Slack delivery

Visit /reports → pick a cadence + a closed period → AI-drafted commentary you can edit → preview + publish to Slack or save as PDF. Replaces the manual Monday-morning leadership memo.

Stage 1 (Configure): Pick weekly / monthly / quarterly / year-end + the period. Periods are listed only for closed cycles — never mid-period.
Stage 2 (Review): 4 headline KPIs (Revenue, Units, Email, Subscribe & Save) + 5 operational (New Customers, Orders, CVR, Sessions, AOV) + Progress Against Goals (Month / Quarter / Year columns paced against your progressive weekly schedule). AI commentary generated by Claude Sonnet on first visit; persisted in report_commentary so revisits don't re-pay (~$0.075/run).
Stage 3 (Preview): Print-mode CSS, PDF filename = report period; Publish to Slack via Block Kit (env-gated on SLACK_WEBHOOK_URL).
4-4-5 retail fiscal calendar (Sprint 3.36): Month/Quarter columns use the standard retail calendar (M1=W1–W4, M2=W5–W8, M3=W9–W13, …). Every fiscal month ends on a Saturday; quarters are exactly 13 weeks. Pacing colors compare against weeks-completed/weeks-in-period. Year column always shows full fiscal $20M annual goal.
AI Insights

Last Week's Intelligence (home page)

Live Sonnet-generated insights — refreshed Mon 7 AM PT

The Intelligence Feed at the top of / is now live AI-generated summary of the most recent completed fiscal week. Replaces the old hardcoded sample feed.

Source: Anthropic Sonnet (claude-sonnet-4-5-20250929)
Cadence: Mon 7 AM PT cron (/api/cron/weekly-insights) — generates insights for the most recent completed fiscal week. Persisted in weekly_insights table.
Inputs: Target week's weekly_snapshots + prior 4 weeks (WoW context), top 5 paid campaigns (Meta + Google) by spend, top 5 Klaviyo flows by RPR. Marketing-calendar context via Asana coming in v2.
Output: Headline + 3–5 summary bullets + 3–5 wins (severity GOOD) + 3–5 watch items (ALERT/WATCH) — each with optional dashboard href drilling into the relevant sub-page.
Cost: ~$0.075/run × 52 weeks ≈ $4/year.
Distinct from alerts: insights describe last week. Alerts (coming soon) describe live thresholds.
AI Insights

Daily Revenue Anomaly Annotations

Dots on /revenue daily chart — probable cause for big +/− days

When a day's revenue moves more than ±15% from the 7-day rolling average, the dashboard surfaces a probable-cause note as a small color-coded dot above that bar on the /revenue daily chart. Hover the dot to see the headline + narrative.

Source: Anthropic Sonnet (claude-sonnet-4-5-20250929)
Cadence: Every 6 hours (/api/cron/daily-insights). Triggers only when |actual − rolling7DayAvg| / avg ≥ 15%.
Inputs: Yesterday's daily_snapshots + 14 days prior context, same-day Klaviyo campaign sends, Meta + Google daily spend deltas, Recharge renewal-wave size.
Output: Severity (alert / watch / good) + headline + narrative + 1–3 contributing factors with attribution percentages.
Cost: ~$0.025/triggered day. Most days don't trigger — typical year cost ≈ $5–10.
Notifications

Scheduled Reports + System Alerts (/alerts inbox)

Slack delivery + system-health monitoring + unified inbox

The /alerts page is a notification inbox with a 🔔 bell + unread badge in the header. Alerts are framed as notification config rather than threshold rules — threshold rules are coming soon.

Scheduled reports (Mon 7 AM PT): Weekly + Unit reports always; Monthly + Monthly Unit on first Monday of month; Quarterly on first Monday of quarter; Year-End on first Monday of October.
System alerts (hourly): Cron-run health checks watch every cron's cron_run_log entry; alerts fire when a run fails or exceeds duration thresholds.
Slack delivery: Each notification dispatches to Slack via SLACK_WEBHOOK_URL (graceful no-op when env missing). Configure at /settings/notifications with a "Send test" button.
Threshold-rule alerts (deferred): Revenue >15% below goal, renewal <58%, email attribution <25%, etc. — configurable per your needs.

Data Accuracy

Every metric is validated against its source platform before shipping.

Accuracy Standards

Revenue Exact match
Dashboard revenue matches Shopify's "Net Sales" figure exactly using the order_net_sales field — after discounts, before shipping and tax.
Sessions Matches Shopify Analytics
Bot-filtered sessions pulled via ShopifyQL API — the same data visible in Shopify Admin > Analytics, with bot traffic explicitly excluded.
Renewal Rate Customer-level precision
Customer-level churn check: for each cancelled customer, we verify if they have zero remaining active subscriptions. Same-day cancel/reactivate excluded.
Subscription Counts Within 1–6%
New and cancelled subscription counts align within 5–6% of platform numbers, driven by timing differences at date boundaries. Same-day create-and-cancel pairs are excluded from both sides.
Units Sold Expected ~15% weekly delta vs NetSuite
The dashboard counts units by order date; NetSuite counts by ship date. Any given week the two views differ by ~10–15% because orders placed Friday–Saturday ship Monday–Tuesday. The gap converges to under 1% over quarterly and YTD periods.
Klaviyo Email Revenue Within 0.3% on settled weeks
Settled weeks (≥3 weeks old) match Klaviyo's Business Performance Summary within 0.3%. The most recent 2–3 weeks may run slightly high until Klaviyo attribution settles (5-day email open/click window).

Monthly Costs

What it takes to keep the data pipeline running.

$99
Windsor.ai — data connectors
$0
Supabase — database (free tier)
Pro
Vercel — hosting and daily cron
~$12
Hetzner VPS — weekly renewal-rate job