How a 4-location phone repair chain went from guessing at profit to knowing it per category, per store, per day
Client: Multi-location phone repair and device sales business, Hawaii (4 stores)
Scope: Attribution dashboard, AI chatbot, CRM migration
Timeline: 5 weeks contracted build, 148 hours
The problem
They were running four stores off RepairDesk and had no reliable way to answer basic questions. Which category actually makes money. Which ad spend turns into walk-ins. Which customers haven’t come back in six months.
The reporting inside RepairDesk gave them revenue, but not profit by category, and not anything tied to marketing. Ad spend on Google and Meta went out the door every month with no line connecting it to a repair ticket. Leads came in through Instagram DMs, Facebook Messenger, WhatsApp, and two custom forms on the website, and most of them landed in the CRM with no source attached.
They’d tried pulling reports manually. It took hours, the numbers never reconciled, and nobody trusted them enough to act on.
What we found in the audit
The first real discovery was a data bug nobody knew about. RepairDesk’s bulk invoice endpoint silently strips cost price and item type from the response. Every profit calculation built on that endpoint was wrong, and there was no error to tell you so.
We also found that only about 27% of invoice line items carried a SKU that matched the inventory catalog, which meant cost of goods had to be resolved through a fallback chain rather than a straight lookup.
What we built
The data pipeline. Four store sync workflows in n8n, one per location, pulling invoices one at a time in concurrent batches instead of using the broken bulk endpoint. Daily at 6AM Hawaii time. Everything lands in Supabase, with transaction logs and inventory catalog on their own syncs.
Category logic. We sat down with the owner and ops lead and agreed on 8 categories that match how they actually think about the business: LCD, Phones and Trade-In, Cases, Accessories, Screen Protectors, Small Parts and Batteries, Special Order, Activations and Bill Payment. Classification runs as a SQL function with ordered pattern matching, because a “tempered glass back camera case” will match three categories if you let it.
The dashboard. Next.js front end on Vercel, store selector, everything visible on load. No accordions, no chevrons, no “show more.” If you have to click to see a number, you won’t look at it.
Ad attribution. Google Ads campaign metrics sync directly into the warehouse. Every lead that hits the CRM gets its raw campaign ID resolved to a human-readable campaign name and tagged. Website forms were custom JavaScript widgets, not CRM iframes, so we intercepted the fetch call and appended UTM parameters to the payload before it posted. Meta and Instagram run through a separate pipeline, since Instagram only delivers webhooks to one app and the CRM already owned that slot.
Win-back campaign. Pulled 846 unique customers from 18 months of repair history. Matched 580 by phone against the CRM, which surfaced 266 customers who had spent money in-store and were completely missing from the CRM. Each contact got 9 spend-by-category fields plus a top category flag, so the follow-up could reference what they actually bought. Four-touch cadence across SMS, WhatsApp, email and Messenger on days 1, 3, 5 and 7.
AI chatbot. Handles pricing questions across WhatsApp, Instagram and Messenger, pulling live from a pricing sheet rather than a hardcoded list. Knows bundle pricing rules, knows store hours, and has margin guardrails so it can’t quote below cost.
Results
- 266 paying customers recovered into the CRM who were previously invisible
- Profit visible by category, by store, by day, replacing a manual reporting process that took [X hours/week]
- Ad spend now traceable to individual leads across Google, Meta and Instagram
- [X%] of inbound pricing questions handled by the chatbot without staff involvement
- Delivered in 148 hours against a 148 hour estimate
What made it work
Two things. First, we treated the audit as real work instead of a formality, which is the only reason the RepairDesk bulk endpoint bug got caught before it poisoned six months of reporting. Second, every category definition came from the client, not from us. The owner and the ops lead signed off on all 8 before a line of SQL got written, so when the dashboard went live nobody argued with what it said.