For Kiran (data + engineering) · prepared by Sebastian, 2026-08-26 · window 2026-07-26 → 2026-08-24 (30 days) · all figures USD
Triple Whale is now the CEO-facing source of truth for both business and Meta numbers. Reconciling it against Shopify and the Meta Marketing API to make that safe turned up five pipeline issues. Two of them silently corrupt numbers that are already being quoted.
The good news first, because it matters: TW's revenue agrees with Shopify to within 2.7% (US$162,839 vs US$158,479) and its Meta spend agrees with the Marketing API to within 1.5% once AUD is converted at ~0.72. On the week ending 2026-08-21 the two agree on attributed purchases exactly (20 and 20). The core of the pipeline is sound. What follows are specific, fixable defects around it.
This came out of the reconciliation and reframes everything downstream.
Google Ads is 63% of paid spend — more than twice Meta. It is easy to miss because it
is the one channel that does not live in a *Spend column: it is
ga_adCost.
| Channel | Spend (30d) | Share | TW column |
|---|---|---|---|
| Google Ads | US$39,975 | 63% | ga_adCost |
| Meta | US$17,703 | 28% | fb_ads_spend |
| TikTok | US$5,566 | 9% | tiktok_spend |
| Total paid | US$63,244 | 100% | adsSpend reports $0 |
| Day | Google Ads | Meta | TikTok |
|---|---|---|---|
| 2026-07-26 | US$1,208 | US$775 | US$56 |
| 2026-07-27 | US$913 | US$1,009 | US$947 |
| 2026-07-28 | US$1,063 | US$793 | US$314 |
| 2026-07-29 | US$1,228 | US$788 | US$287 |
| 2026-07-30 | US$1,115 | US$386 | US$404 |
| 2026-07-31 | US$1,195 | US$820 | US$566 |
| 2026-08-01 | US$1,176 | US$1,000 | US$151 |
| 2026-08-02 | US$1,065 | US$1,109 | US$294 |
| 2026-08-03 | US$962 | US$1,013 | US$588 |
| 2026-08-04 | US$1,284 | US$938 | US$177 |
| 2026-08-05 | US$1,268 | US$861 | US$132 |
| 2026-08-06 | US$1,639 | US$816 | US$102 |
| 2026-08-07 | US$1,781 | US$792 | US$136 |
| 2026-08-08 | US$1,714 | US$554 | US$123 |
| 2026-08-09 | US$1,307 | US$441 | US$107 |
| 2026-08-10 | US$1,309 | US$500 | US$70 |
| 2026-08-11 | US$1,152 | US$348 | US$46 |
| 2026-08-12 | US$1,210 | US$360 | US$54 |
| 2026-08-13 | US$1,311 | US$433 | US$48 |
| 2026-08-14 | US$1,394 | US$381 | US$116 |
| 2026-08-15 | US$1,518 | US$322 | US$129 |
| 2026-08-16 | US$1,340 | US$270 | US$71 |
| 2026-08-17 | US$1,184 | US$388 | US$47 |
| 2026-08-18 | US$1,792 | US$419 | US$112 |
| 2026-08-19 | US$2,097 | US$352 | US$77 |
| 2026-08-20 | US$1,376 | US$338 | US$98 |
| 2026-08-21 | US$1,662 | US$365 | US$90 |
| 2026-08-22 | US$1,473 | US$322 | US$105 |
| 2026-08-23 | US$1,194 | US$381 | US$63 |
| 2026-08-24 | US$1,045 | US$429 | US$56 |
adsSpend returns $0 on every daycorrupts live numbersThe per-channel fields are populated, but the aggregate field is not. Anything computed off total ad spend — blended ROAS, MER, blended CAC — is therefore dividing by zero or falling back to something undocumented. These are exactly the metrics a CEO dashboard leads with, which makes this the one to fix first.
adsSpend reports, against what the channel fields sum to, over
the same 30 days.Ask: is adsSpend mapped in the connector at all? If it is
meant to be derived, deriving it as ga_adCost + fb_ads_spend + tiktok_spend would
land within ~7% of blendedAds (US$67,412), which does look correct.
blendedSales is 27% above totalSalesneeds a definitionblendedSales sums to US$206,705 against
totalSales of US$162,839 over the same 30 days — and above Shopify's
gross sales for the period, so it is not simply gross-vs-net. It may well be a legitimate TW
definition, but it is undocumented, and until someone can say what it counts neither figure is
safe to put in front of Gabe.
Ask: what does blendedSales include that
totalSales does not?
| Table | Rows | Schema | Last modified (Sydney) |
|---|---|---|---|
raw.shopify_order_attribution | 0 | valid — 6 cols (order_id, session_id, quiz_flow_id, …) | 2026-08-17 11:32 |
raw.quiz_events_utm | 0 | valid — 17 cols incl. utm_source/medium/campaign/id | 2026-08-10 15:27 |
Both have correct schemas and no rows, so this reads as an ingest that never ran rather than a modelling error. With both empty, paid attribution cannot be closed from the warehouse at all.
There is very likely a shared root cause upstream, and it is visible from the Shopify side: Meta ad URLs appear to carry no UTM parameters. Meta sent 8,330 link clicks in this window; Shopify classified only 82 sessions as paid, and logged Meta's traffic as ordinary social referrals instead. If that is right, there is nothing upstream for either table to ingest, and fixing the UTMs fixes the pipeline and the attribution together.
Duplicated: 2026-07-01 and 2026-07-02 each appear twice, and 07-02 was
restated between loads (US$6,108 → US$5,682) with no dedup on loaded_at.
Any consumer that does not dedup will double-count those days.
Missing: 2026-06-29 and 2026-07-21 are absent entirely.
Short: the series begins 2026-06-23, so there is no quarter-on-quarter comparison, let
alone year-on-year.
Ask: can the loader dedup on latest loaded_at, backfill the
two gaps, and is a longer history available from the TW API?
On rounding: the daily table rounds each day to the dollar, so its column sums land within ~$2 of the unrounded aggregates (US$63,246 total paid, US$39,977 Google). Query the warehouse directly for exact figures.
Every figure above comes from
lucky-nature-498502-h2.raw.triple_whale_summary unless stated otherwise, deduped
on latest loaded_at. Run these to check any of it.
SELECT ROUND(SUM(ga_adCost),0) AS google,
ROUND(SUM(fb_ads_spend),0) AS meta,
ROUND(SUM(tiktok_spend),0) AS tiktok,
ROUND(SUM(adsSpend),0) AS ads_spend_field,
ROUND(SUM(blendedAds),0) AS blended_ads
FROM `lucky-nature-498502-h2.raw.triple_whale_summary`
WHERE DATE_DIFF(DATE(date_end), DATE(date_start), DAY) = 0
AND DATE(date_start) BETWEEN '2026-07-26' AND '2026-08-24';SELECT table_id, row_count,
TIMESTAMP_MILLIS(last_modified_time) AS last_modified
FROM `lucky-nature-498502-h2.raw.__TABLES__`
WHERE table_id IN ('shopify_order_attribution','quiz_events_utm');SELECT DATE(date_start) AS day, COUNT(*) AS n,
STRING_AGG(CAST(ROUND(totalSales,0) AS STRING), ' | ') AS variants
FROM `lucky-nature-498502-h2.raw.triple_whale_summary`
WHERE DATE_DIFF(DATE(date_end), DATE(date_start), DAY) = 0
GROUP BY day HAVING COUNT(*) > 1;
In priority order: (1) fix or derive adsSpend — it is
breaking metrics already on screen. (2) confirm the UTM situation on ad URLs, which I
believe is the root cause of findings 3 and 4. (3) define
blendedSales. (4) dedup and backfill the daily series.
Happy to take any of this to the Triple Whale side rather than yours if that is the faster path — I mainly need to know which numbers I can safely put in front of Gabe in the meantime.
Generated
2026-08-26 by database/tw_data_quality_report.py. Timestamps Australia/Sydney;
BigQuery stores UTC.