Kalen asked if switching BSP off ServiceTitan to something like Square would give cleaner data for Google Ads attribution. This document answers that question with the full decision brief, API comparison, architecture diagrams, and a 3-phase action plan.
Kalen's instinct that our data is broken is 100% correct. But the diagnosis is one layer off. The problem isn't "ServiceTitan is garbage, we need to switch." The problem is we are asking ServiceTitan the wrong question at the wrong moment. We poll for job_status = 'Completed' the second a job flips to complete in the field, when the invoice is still $0. The invoice posts hours or days later when Ashton closes it out. Switch from polling to webhooks, listen for invoice.updated instead of job.completed, and the data becomes clean overnight at zero incremental cost.
We've been blaming ServiceTitan for "broken data" when the real issue is a timing bug in what events we listen for. Here is the broken path versus the fixed path, side by side.
job_status = Completedinvoice.total is still $0 because the invoice hasn't been built yettitan.jobs with invoice_total=0invoice.updatedinvoice.updated event (NOT job.completed)invoice.total, line items, job ID, customer, and our GCLID custom fielduploadClickConversions with real $7,000 valueHere is what a clean attribution stack looks like for BSP. Three layers, each with one job.
Here is every platform we evaluated, in order from cheapest/fastest to most expensive/slowest. Each card shows the API capability, cost, speed to clean data, biggest risk, and verdict.
BSP already has ST. The current sync script polls ST's REST API, which is slow and times events wrong. ST's Developer Portal exposes a full Webhooks API. Subscribe to invoice.updated instead of polling, and the pipeline goes from broken to clean at zero incremental cost.
invoice.updated for the true amount.| Developer portal | developer.servicetitan.io |
| Event types | job.*, invoice.*, appointment.*, lead.*, customer.* |
| Delivery | HTTPS POST with HMAC signature |
| Parallel run | N/A โ same system, just push instead of poll |
| Monthly cost | $0 |
| Time to clean data | 3 to 5 days |
Square has a clean REST API and custom attributes where GCLID can be stored. As an attribution-grade revenue ledger running parallel to ST, it's excellent. As a full ST replacement, it's missing dispatch, pricebook, memberships, and tech commissions โ which is where ST actually earns its keep.
| Developer portal | developer.squareup.com/reference/square/bookings-api |
| Bookings API | POST /v2/bookings, GET /v2/bookings/{id} |
| Invoices API | POST /v2/invoices + webhooks on invoice.payment_made |
| Custom attrs | developer.squareup.com/docs/customer-custom-attributes-api |
| Parallel run | Yes โ very doable, 30 day A/B test |
| Monthly cost | ~$120 software + processing fees ($75-90K/yr on $3M) |
| Time to clean data | 2 weeks |
Jobber Grow ($349/mo) is the only platform in this list with a native Google Ads conversion tracking integration. That's a killer feature for Smart Bidding. But Jobber is built for 1-15 person residential shops โ scaling to $6M with Kalen's pricebook complexity will hit ceilings.
Public REST API exists but is notoriously limited compared to Jobber or Square. Has a Google Ads integration on MAX plan only. Developer community consistently reports the API lags behind the UI.
REST API at api.workiz.com, thinner than Jobber/Square. Markets a "Genius Ads" product but the ecosystem is smaller and less proven at $3M+ scale.
The ranking by speed to clean Google Ads data. Note: the #1 answer was NOT in the original 5 options โ the research surfaced a missing piece called WhatConverts that every Smart Bidding pro uses.
Aggregated from Service Business Mastery podcast, Home Service Millionaire forum, r/PPC threads, and case studies from Smart Bidding pros. The common pattern is consistent: the CRM is NOT the attribution source of truth. It's the operational system. Attribution lives in a separate middleware layer that sits between the CRM and Google Ads.
Don't pick one option. Do all three, in this order, each delivering value before the next starts.
invoice.updated (not job.completed). Listener receives webhook, reads invoice.total + GCLID custom field, fires Google Ads uploadClickConversions with real dollar amount. HMAC signature verification baked in. New table titan.invoice_events for audit trail. Zero new software cost. Zero ST disruption.
Kalen's frustration is 100% legitimate. The data IS broken. But the solution is not a platform migration. Here is why ripping out ST is the wrong call right now, stated plainly.
invoice.updated and you get a clean stream of real revenue events tied to real jobs. The 71% zero-invoice rate disappears because you're no longer asking "what's the invoice total the second the job completes in the field" โ you're asking "what's the invoice total the second Ashton posts the invoice." Two different questions. Two different answers.