Each card shows a key business metric with a status color (healthy / watch / needs help / informational) and a button to fire the matching automation. Connect data sources to populate the numbers.
Charts that populate once tenant data is connected:
Monthly new customer trend
How customers found the business (lead source attribution)
Top ZIPs and neighborhoods for new customers
Customer loyalty tiers (jobs completed bands)
Year-over-year growth (signup year, recurring vs one-time)
Subscription plan mix detail
Cohort retention by signup year
Seasonal signup pattern
Historical charts render once data sources are connected.
📊 Average Stop Revenue
Basic intelligence about your routes — revenue per stop by day, by route, by ZIP. Identifies which routes generate the highest per-stop revenue. Connect data sources to populate.
📊 Revenue per Stop — By Day
No data — connect data sources to populate
📍 Revenue per Stop — By ZIP (Top 10)
No data — connect data sources to populate
📋 Revenue per Stop — By Service Type
No data — connect data sources to populate
📉 Route Profitability
Cost-per-stop comes from your operating expenses divided by total stops. Revenue comes from your payment data. Built for ownership decisions: keep the route, restructure it, cancel it, or adjust frequency. Connect data sources to populate.
📈 Route Profitability by Day and Route
📅 Daily Route Composition — What's Inside Each Route
⚠ Losing Routes — Cycle-Week Detail
📍 ZIP-Level Profitability
🔎 Decision Support — Where Action Is Needed
⚙ Route Inputs — Data Sources for Profitability Calculation
Route Profitability needs accurate inputs. This is where the data comes from. For multi-tenant deployment, this is where each business owner uploads their own numbers.
📊 Required Inputs — What Profitability Math Needs
Total Operating Expenses (year-to-date): Payroll, fuel, vehicle, insurance, software, rent. Pulled from accounting (QuickBooks, Xero, etc.) or uploaded manually.
Total Stops Completed (year-to-date): Number of customer service stops actually performed. Pulled from field-service software (GorillaDesk, Jobber, etc.) or uploaded.
Revenue per Stop (by day & route): Total revenue collected at each stop. Pulled from payment processor (Square, Stripe, etc.) or uploaded.
Route Schedule (day-by-day): Which routes run which days, how many trucks, how many runs per cycle. Pulled from field-service schedule.
Cycle-Week Rotation: If you use a 4-week rotating route schedule, the system needs to know which customers fall in which week.
📂 Connected Data Sources (For Your Tenant)
For the active tenant, these data sources are connected:
Payment processor:Status pending — will show: Connected / Disconnected once wired
Accounting:Status pending
Field-service software:Status pending
💾 Manual Upload (For New Tenants Without API Connections)
If a tenant doesn't have API-connected sources, upload the four required inputs as CSVs. Each tile accepts one file and shows status. Click Download Template to grab a starter file with the right column headers.
💵
Operating Expenses
Annual P&L breakdown by category
Pending
No file selected
🚚
Stops Completed
Year-to-date stops with date + route
Pending
No file selected
💰
Revenue per Stop
Total collected per service date
Pending
No file selected
📅
Route Schedule
Cycle-week rotation, days, trucks
Pending
No file selected
0 of 4 files uploaded
CSV files are parsed client-side first for column-header validation. On Apply, they upload to the tenant\'s data layer and trigger a recalculation of the Route Profitability tab.
🔗 Integrations — Live Data Sources
Drop GorillaDesk exports here and the dashboard refreshes live. No hardcoded numbers, no D1 cache — what you upload is what you see. Future API connections (WooCommerce, Square, QuickBooks) plug in here too.
📤 Drop GorillaDesk Export
📤
Drop a GorillaDesk CSV here
or click to choose a file
Supported: new_customers (*).csv from GorillaDesk export
✅ Currently Loaded
No data loaded. Drop a file above.
📊 QuickBooks — Live API
Click "Pull QB P&L" to fetch live operating-expense data from QuickBooks. This unlocks Route Profitability Margin (KPI 3).
🔒 Other Live API Connections
🛒 WooCommerce Loading…
Connecting to WooCommerce…
◯ Square Loading…
Connecting to Square…
📊 QuickBooks Live
Connected via occ-bi-api worker
🚚 GorillaDesk Live (CSV)
Source of truth for: route schedule, job status, completed cleanings
💎 Customer Lifetime Value
Estimated total revenue you'll earn from a customer over their full relationship with you. Calculation method: Average revenue per customer per year ÷ annual churn rate. Industry-standard formula. Connect data sources to populate.
💎 Lifetime Value Summary
Connecting to lifetime value endpoint…
Median Lifetime Value
—
across active customers
Top Decile Lifetime Value
—
90th percentile
Average Tenure
—
months active
Total Active Lifetime Value
—
portfolio value
🏆 Top 50 Customers by Lifetime Value
Click "Sync" to load top customers from live data
📊 Lifetime Value by Plan Type
Plan-type breakdown loads after sync
📊 Per-Customer P&L
Profit and loss broken down by individual customer. Revenue from payments minus allocated cost-to-serve (route share + extra-can handling + missed visits). Identifies the profitable customers, the marginal customers, and the unprofitable customers you may need to re-route, re-price, or release. Connect data sources to populate.
💸 Profitability Distribution
No data — connect data sources to populate
Profitable
—
contribution margin > $0
Marginal
—
margin between −$50 and $50
Unprofitable
—
contribution margin < -$50
Avg Margin / Customer
—
trailing 12 mo
⚠ Bottom 25 by Contribution Margin
🏆 Top 25 by Contribution Margin
Unprofitable customers in re-routable ZIPs → Route Profitability decisions
🔄 One-Time Customer Conversion
One-time customers tracked through their conversion window. The funnel: Day 0 (just had service) → Day 80 (final touch). After Day 80 with no conversion to recurring, they move to the Lost bucket.
🔄 Conversion Funnel
Connecting to one-time conversion endpoint…
In Funnel (Day 0–80)
—
awaiting conversion
Converted
—
one-time → recurring
Conversion Rate
—
trailing 90 days
Lost (past Day 80)
—
no conversion
Funnel Stages
Funnel stage breakdown loads after sync
💰 Pricing Intelligence
Compares your prices to comparable markets — same population size, similar income level, similar competition density. Tells you if you're under-priced (room to raise on new signups), at market (good), or over-priced (hurting conversion). The comparable-market dataset is research-driven; the framework is built and waiting for the research to be loaded.
📋 Pricing Catalog — Per-Cleaning Rates by Plan and Can Count
Your published rate matrix. New signups quote from this. Existing grandfathered customers stay at their original rate (locked rule).
No data — connect pricing source to populate
📊 Internal — Catalog Price vs Realized Average (Per Plan)
Catalog price = your published rate per cleaning. Realized average = what you're actually collecting per stop. Gap reveals grandfathered customers, prepaid bonuses, or pricing erosion.
No data — connect data sources to populate
🔎 Comparable Market Analysis — Research Pending
What this will show when the research dataset is loaded:
Comparable-market pricing range per plan/can-count combination
Your position: under market / at market / over market — by percentage
Estimated annual revenue opportunity if catalog rates align with market for new signups
Research methodology:
Match comparable markets by: city/town size, residential density, median household income, population
Pull actual competitor pricing per plan/can-count from those markets (not approximated, not guessed)
Filter to markets with similar income profile (avoid comparing high-income suburb to low-income market)
Status: Research project on the build queue. UI shell ready to receive the dataset.
💡 Decision Support — Pricing Actions
Locked rules (cannot be overridden by recommendations):
Catalog price applies to NEW signups only. Existing grandfathered customers stay at their original rate.
Bulk price increases on existing customers require explicit timing approval — never automated.
Specific price-action recommendations populate here once both the internal data is connected and the comparable-market research is loaded.
🎯 Win-Back Campaign Active — Manual Fire
6 months after cancellation, sends a single check-in email with three options: Sign Back Up, Not Interested, or Opt Out. Manager reviews responses and dismisses. Not automated — Howard fires manually.
⚙ Campaign Status
No data — connect to live customer database to populate
Cancelled 6+ Months
—
eligible for check-in
Sent This Run
—
check-ins delivered
Signed Back Up
—
reactivated
Opted Out
—
suppressed permanently
📋 How It Works
Trigger: Customer cancelled and has been gone for 6 months.
Touch: One check-in email. Three response buttons: Sign Back Up / Not Interested / Opt Out.
Dismissal: Manager reviews responses and dismisses each one manually. Not auto-processed.
Not automated: Howard fires this manually when ready. No drip sequence, no multi-touch, no discount escalation.
⚙ Activation Controls
Last fire: never · Last sign-up: never
📈 Active Dormant Upsell Manual Fire
When the truck is in a customer’s neighborhood, sends a proximity-triggered email/SMS: “We’re in your neighborhood [date], want an add-on?” Fires 7 days before truck day. Targets dormant customers near that week’s route. 90-day throttle prevents duplicate sends.
⚙ Campaign Status
No data — connect to live route + customer data to populate
Proximity Eligible
—
dormant near this week’s route
Sent This Run
—
after 90-day throttle
Responded
—
booked add-on
Suppressed
—
within 90-day window
📋 How It Works
Trigger: Truck route enters a customer’s neighborhood. Email/SMS fires 7 days before the truck day.
Message: “We’re in your neighborhood [date], want an add-on?”
Throttle: 90-day suppression — each customer receives at most one touch per 90 days.
Backend:occ-command-center → nt_lapsed_send
⚙ Activation Controls
Last fire: never
💳 Payment Follow-Up Worker OFF — Per Master State
Worker: occ-payment-followup. Currently kept OFF per master state. Shows declined cards (from Square live data) and overdue AR. Charge timing and missed-fee rules are locked.
⚙ Worker Status
Worker OFF — kept off per master state
Declined YTD
—
from Square live data
Overdue AR
—
unpaid invoices
Recovered MTD
—
—
Active Dunning
—
—
📋 Locked Rules
Charge timing: Authorize at crew check-in, capture in the evening batch.
Missed-cleaning / fuel fee: Tracking and messaging run automatically. Actual dollar charge requires Howard’s approval per incident — never auto-charged.
⚙ Activation Controls
Last fire: never
🔁 One-Time Customer Drip Active — Daily 9 AM ET
Worker: occ-ot-followup. Runs daily at 9 AM ET. Three emails convert one-time customers to recurring plans: Day 3, Day 21, Day 80.
⚙ Worker Status
Active — occ-ot-followup running daily 9 AM ET
In Drip
—
currently active
Converted MTD
—
one-time → recurring
Email Open Rate
—
drip avg
Avg Time to Convert
—
days from one-time job
📋 Drip Schedule
Day 3 — Email touch 1
Day 21 — Email touch 2
Day 80 — Email touch 3 (final)
Worker: occ-ot-followup · Cron: daily 9 AM ET
⚙ Activation Controls
Last fire: daily 9 AM ET
🤝 Referral Engine Active — Daily 10 AM ET
Worker: occ-referral-pitch. After a customer’s 3rd completed service, automatically sends their unique referral code and explains the referral program. Runs daily at 10 AM ET.
⚙ Worker Status
Active — occ-referral-pitch running daily 10 AM ET
Active Agents
—
referral partners
Total Referrals
—
all time
Referrals Activated
—
new signups from codes
Revenue from Referrals
—
attribution required
📋 How It Works
Trigger: Customer completes their 3rd service.
Action: Worker sends their unique referral code and explains the referral program.
Worker:occ-referral-pitch · Cron: daily 10 AM ET
⚙ Activation Controls
Last fire: daily 10 AM ET
Editor
Editor
Saved — will persist once data sources are connected