← Back to case studies
Case Study9 min read·September 2026

Phone Repair Attribution Dashboard: TCA Wireless Case Study

AA

Agentum AI Team

Agentum AI

phone repair marketing attribution dashboard

TCA Wireless: Tracing Every Ad Dollar to a Paid Invoice Across Four Stores

Industry: Phone repair, device buyback and retail Location: Oahu, Hawaii Build: Attribution dashboard, RepairDesk to GoHighLevel to Supabase to Next.js Timeline: 5 weeks, 148 hours contracted

4 store pipelines synced · 4 platforms attributed · 846 customers backfilled · 0 manual reports

The problem

TCA Wireless runs phone repair, trade-in and device sales out of three retail locations on Oahu plus a warehouse, under the Aloha Phone Repair brand. They were spending real money on Meta and Google every month. They also had leads arriving through Facebook messages, Instagram DMs, WhatsApp, website forms, phone calls and people just walking in the door.

The sales lived in RepairDesk. The leads lived in GoHighLevel. The ad spend lived in Meta Ads Manager and Google Ads. None of those four systems talked to each other.

So the question every owner asks, which campaign is actually making me money, had no answer. You could see what Meta charged you last week. You could see that revenue was up. You could not see whether one caused the other. Nobody could tell you the cost per lead for a specific campaign, or which of the three stores a Facebook lead ended up buying from, or whether the buyback ads were pulling their weight against the repair ads.

That is the gap we were hired to close.

What we built

One dashboard. It opens, it is current, and it shows where the marketing money went and what came back.

The pipeline underneath it runs like this. Four RepairDesk sync workflows, one per location, pull invoices into Supabase every morning at 6AM Hawaii time. GoHighLevel pushes leads in as they arrive, tagged with the campaign that produced them. Google Ads and Meta spend sync in against those same campaigns. A Next.js front end reads the joined data and renders it.

The owner does not touch any of it. There is no export step, no spreadsheet, no Monday morning report to assemble. Julie opens the dashboard and the numbers are already there.

The four step attribution funnel

The centerpiece is a funnel that follows a customer from the first ad click to the invoice they pay at the counter.

Ad conversations. Someone clicked a Meta ad and sent a message, or clicked a Google ad and filled in a form. First contact, caused by a specific ad.

Leads in CRM. The contact record in GoHighLevel, deduplicated. A person who messages on Instagram and later calls the shop counts once, not twice.

Booked jobs. The lead moved to a confirmed stage in the pipeline. They committed to bringing the phone in.

Invoiced. The repair or buyback happened and RepairDesk generated a paid invoice. Real money, closed, not a quote sitting in limbo.

Conversion rates sit between each step, so you can see exactly where people fall out. If 100 people message and 45 end up invoiced, that is a 45% close rate and you know it without asking anyone.

Cost per lead and ROAS, per campaign

Above the funnel sits the numbers the owner actually argues about in meetings.

Attributed revenue counts only closed, paid RepairDesk invoices belonging to customers who came through a marketing channel. Quotes do not count. Leads that never converted do not count. ROAS divides that revenue by the ad spend for the same window, so 4.2x means every dollar spent returned $4.20.

Underneath, a campaign table breaks it down row by row. Each ad campaign shows its spend, leads, cost per lead, booked jobs, revenue and ROAS, grouped by platform with platform totals on the header row. Campaigns with spend and zero leads mean the ad is running and nobody is clicking. Campaigns with leads and zero revenue mean people are responding and not buying. Two very different problems, two very different fixes, and now they are visible in the same table instead of being guessed at.

Platform cards do the same thing one level up, comparing Meta, Google, TikTok and a Direct and Other bucket that catches walk-ins, phone calls and organic website traffic. That last bucket matters more than people expect. In a business with three physical stores, a large share of revenue never touches an ad, and if you do not measure it you will overcredit whatever campaign happened to be running.

Revenue by store and service type

Revenue splits two more ways. By location, so Kaheka, Pearl City and Waipahu each show their revenue, lead count, booked jobs and share of the total. And by what was actually sold: repair, buyback, phone sale or accessories.

That second split is the one that changes decisions. A shop that assumes it is a repair business and discovers buybacks are a third of revenue starts advertising differently. We built a separate profit view for the category level economics, which is covered in our multi-location profit dashboard case study.

There is also a leads by intent view, splitting inbound by what the person wanted: a repair, a trade-in, or unclassified when the first message does not make it clear. Each shows lead count, conversions and close rate, which turns out to be a decent read on how well the counter staff handle each type of inquiry.

Filters that actually filter

Date range, store and campaign. Today, 7 days, 30 days, 90 days. Change any one of them and every number on the page updates together: revenue, spend, leads, funnel, charts, tables, all of it. Pick Pearl City and you get Pearl City’s ads, Pearl City’s leads and Pearl City’s revenue.

Every date runs on Hawaii time, which sounds trivial and is not. More on that below.

Knowing whether to trust the screen

A dashboard that silently stops updating is worse than no dashboard, because people keep making decisions from it. So the footer shows the timestamp of the most recent lead or invoice recorded, and the header carries a set of data coverage indicators, one per source. Green means data is flowing. Grey means that source is not connected. If a sync breaks, you can see it in two seconds instead of finding out three weeks later that the numbers have been wrong.

The technical problems worth writing down

Most of the build time did not go into charts. It went into these.

RepairDesk’s bulk invoice endpoint drops fields. The bulk endpoint silently strips cost_price and item_type from line items. Not an error, not a warning, just missing data, which quietly breaks every profit and trade-in calculation downstream. We found it by reconciling dashboard totals against the closing reports the team runs by hand, and rewrote all four store workflows to fetch invoices individually with concurrent batches of ten. Slower per run, correct every run.

Hawaii time is not a display setting. HST sits at UTC+10 with no daylight saving. Every query boundary, every cron, every daily bucket has to be offset consistently or revenue lands on the wrong day and daily comparisons drift. We aligned the whole pipeline to HST rather than converting at the presentation layer, which is the only version of this that stays correct.

Deduplicating a person across five channels. The same customer might DM on Instagram, message on WhatsApp and then walk in. Counting them three times inflates lead volume and destroys cost per lead. Matching is done on phone number where it exists, with fallbacks, so one human is one lead.

Campaign IDs are not campaign names. GoHighLevel stores the raw ad ID on the contact. A dashboard row reading 23859... is useless to a shop owner. A resolution step calls the Google Ads API, maps IDs to human readable campaign names, and rewrites the tags on the contact record so the CRM is readable too, not just the dashboard.

Website forms were custom, not native. The site’s quote and support forms are custom JavaScript widgets posting to webhooks, not embedded CRM iframes, so UTM parameters were being dropped on submission. We intercepted the submission, appended the UTM fields to the payload and passed all six through to the CRM contact record.

What the team does with it now

Marketing conversations changed shape. Instead of “Facebook feels like it is working,” it is “the buyback campaign is running at half the cost per lead of the repair campaign, move budget.” Instead of guessing which store is carrying the group, it is on screen.

We also used the same data foundation to run reactivation properly. Pulling purchase history out of RepairDesk and matching it against the CRM surfaced 846 unique customers from a three month window. 580 matched an existing contact by phone, and 266 turned out to be genuine gaps, people who had spent money at the store and were not in the CRM at all. We built ten custom fields to hold per category spend history, tagged the matched contacts, and triggered a four touch win-back sequence across SMS, WhatsApp, email and Messenger over seven days. That same infrastructure later carried a promotional campaign to roughly 47,000 contacts, enrolled through a drip rather than blasted, so the sending stayed inside platform limits.

None of that is possible without the attribution layer underneath it. You cannot re-market to your best category buyers if you do not know what anybody bought.

What is still open

We would rather write this down than pretend the build was frictionless.

Meta attribution is incomplete. Most Facebook and Instagram leads message without sharing a phone number, and when they walk into the store later, RepairDesk creates a separate customer record with no link back. So Meta gets undercredited on revenue. The fix is an identity resolution path using the Instagram scoped user ID against the ads API, which is wired and waiting on an access token.

Cost data has a coverage gap. Only about 27% of invoice line items carry a SKU that matches the inventory catalog, so cost prices fall back to category level averages. Profit at the category level is close but not exact until the inventory records are filled in, which is work on the client’s side.

Store tagging is inconsistent. Most invoices currently resolve to the Kaheka location because that is where RepairDesk is tagging them at entry. Until the store field is set reliably on every invoice, the per location split understates the other two shops.

The booked jobs stage of the funnel depends on pipeline stages being maintained in the CRM by the counter staff. A dashboard can only report the discipline that exists upstream of it.

That last point is the general lesson. Attribution is not a reporting problem you can solve by buying software. It is a data collection problem, and about half the work is getting the humans and the tills to record things consistently enough that the reporting has something honest to read.

The stack

n8n for orchestration and every sync workflow. Supabase as the warehouse, with SQL functions handling category classification and profit logic. Next.js and Vercel for the dashboard. RepairDesk for point of sale and invoicing. GoHighLevel as the CRM and lead capture layer. Google Ads API and Meta Graph API for spend and campaign data.

No off the shelf attribution product. The tools that do this well start at four figures a month and none of them integrate with RepairDesk, which is the system the revenue actually lives in.

// Free Audit

Book your free automation audit.

Thirty minutes. No pitch. You leave with a prioritized roadmap for the highest-return automations in your business, yours to keep whether you hire us or not.

✓ No obligation ✓ Reply within 24 hours ✓ Fixed-price or hourly