BCA
BIN CLEANER
AUTOMATION
Business Intelligence
👥 Customer Intelligence
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.
Loading metrics…
🔁 New Customers This Month
No data — new-customer feed not connected.
📊 Supporting Charts — Historical Detail (placeholder)

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
  1. Total Operating Expenses (year-to-date): Payroll, fuel, vehicle, insurance, software, rent. Pulled from accounting (QuickBooks, Xero, etc.) or uploaded manually.
  2. Total Stops Completed (year-to-date): Number of customer service stops actually performed. Pulled from field-service software (GorillaDesk, Jobber, etc.) or uploaded.
  3. Revenue per Stop (by day & route): Total revenue collected at each stop. Pulled from payment processor (Square, Stripe, etc.) or uploaded.
  4. Route Schedule (day-by-day): Which routes run which days, how many trucks, how many runs per cycle. Pulled from field-service schedule.
  5. 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-centernt_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
Saved — will persist once data sources are connected