Internal · not indexed · sanitized

Birthday — GTA · Meta Ads Substrate Dossier

Campaign 120250641116100494 · account act_833808848824841 (CAD) · business 505925625406736 · flight 2026-06-26 → 2026-08-16 (51 days) · compiled 2026-08-16 from the official Meta Ads MCP (read-only) + hub D1 + GA4.

Why this page exists. A peer analysis session has no Meta Ads MCP and no hub D1 connection. This is every number it would otherwise have to take on trust, so it can work from substrate instead of from a summary.

Personal data has been removed. The seven attributed parties are identified by internal hold id only — no names, emails, or phone numbers appear anywhere on this page. The one phone number that matters analytically is described by role, not digits.

Headline state

Lifetime spend
CA$3,008
Meta leads
332
341 reported; one internal test removed; hub sees 333
Attributed parties
7
break-even is 10
Cost per party
CA$430
vs CA$300 margin ⇒ ≈ −CA$900
Four seats have run and each overturned the previous one. Treat every conclusion here as provisional. The document that matters is the open-questions section — assume there is a fifth finding.

B7 + B11 — RESOLVED

B7 — CONFIRMED as a mechanism, REFUTED as the explanation. Sol's chain is literally true and was reproduced end-to-end on live data. But it accounts for 2 of the 29 skipped events, not most of them — and 1 party inside this campaign.

Where non_meta_source is decided — the actual field, quoted

Three hops in boogles-hub @ bf0c44b:

src/conversions/meta-capi.ts:308
    if (isBirthdayConversion && !row.lead_id) → 'skipped:non_meta_source'
src/conversions/meta-capi.ts:286
    lead_id: body?.lead_id ?? customer.lead_id ?? null
src/conversions/birthday-triggers.ts:166   (twin: src/leads/read-source.ts:158)
    LEFT JOIN meta_leads ml ON ... WHERE LOWER(ml2.email) = LOWER(sh.customer_email)

⇒ The determining field is slot_holds.customer_email on the RESOLVED hold, matched by exact lower-cased equality against the Meta lead-form address. No phone lane, no name lane, no cross-hold lineage lane. conversions has no lead_id column at all — the classification is derived at fire time and never persisted.

The mechanism claim is true but the consequence is narrower than predicted. A family re-entering through a second door normally uses the same email, so the join still resolves. What actually breaks it is a CHANGED EMAIL ADDRESS, not a changed door.

The 29 skipped events, resolved by three independent identity lanes

LaneMatches among the 29
L1 exact email0
L2 phone (last-10, business + boilerplate numbers excluded)2 (same person)
L3 name / fuzzy local-part0 additional

MEASURED: 2 of 29 (6.9%) are Meta-originated people whose credit was dropped. 27 of 29 are genuinely non-Meta. Total B7 leakage across both campaigns: 2 parties / 4 conversion events. Within Birthday — GTA: 1 party / 3 events (2 qualified + 1 booked, CA$1,000).

Controls, because three lanes returning near-zero is exactly the shape that lies: the same matcher on the 18 ok events matches 16/18 (all via L1). L2/L3 were then proven separately — across all 483 holds since 2026-06-20 the phone lane fires on 317 holds and the name lane on 331, producing 6 genuine non-email cross-identity matches elsewhere. The non-email lanes demonstrably fire. Phone coverage was recovered for 20 of 22 phone-less holds by deep-scanning all thread messages — so this is an observed absence, not an untested one.

Residual limit (INFERRED, unbounded): a lead submitted by one parent and a booking made by the other with a different email AND phone AND name is undetectable by construction.

Unresolved: two ok events (2026-07-06) have no row in meta_leads or leads. A post-fire emails CASCADE delete is the most plausible cause (resend-guard.ts:16 names that failure mode) but it was not proven.

What the 29 skips actually are

Website form (no-reply@) 13 · customer's own address 10 (8 of them replies to the nurture email) · birthdays-LP 5 · PR-agency referral 1. ⚠ The nurture list is broader than the Meta lead list — none of those 8 repliers has a Meta lead under any identity, so a nurture reply is not a Meta signal.

New findings from the person-level pass

a) Meta is substantially RE-TOUCHING already-lost prospects, not acquiring new ones. 7 of 17 identity-linked multi-hold people had a dead hold that PREDATES their Meta lead — by 11, 19, 23, 37, 40 and 46 days. The two-door pattern runs backwards for them. Any incrementality read on this campaign has to net them out.
d) A merged party fires the qualified conversion TWICE — live defect, independent of everything else. The once-only guard is keyed qualified:<hold_id>per hold, not per party — so a merge defeats it. Both instances here happened to skip, so nothing double-uploaded; a Meta-matched merge pair would send two Leads for one party.

Still open

B1 — Why did R2's video play-rate collapse? highest-value technical

Plays ÷ impressions is 23–33% for R2 against 87–93% for R1 and R4 — and it persists within identical placements (feed 85–88% vs 23–40%; IG stories 99%+ vs ~17%). Video length does not explain a play-start gap.

R2 also beats R1 on CTR in all three ad sets. If R2 is serving a static preview/fallback to a large fraction of impressions, then the static first frame may be the campaign's best click creative — which changes the test from "replace R2 with another video" to "test R2's static treatment against video."

Needs: object_story_spec / effective_object_story_id, the underlying post's attachment/media type, thumbnail and static-fallback configuration, and video-start metrics split by device × platform × placement.

B3 + B4 — Decompose geo × platform × placement × age together, not separately

Post-click conversion fell at SQ1 and Hillcrest but rose at UCM; and within SQ1, Instagram CVR rose slightly while Facebook CVR fell materially. A compositional shift could produce the apparent geo divergence with nothing changing in the form or in underlying demand. Check whether SQ1/Hillcrest shifted toward cells with weaker form completion while UCM shifted the other way. This is one of the few explanations capable of reconciling both at once.

B5 — Bound the attribution uncertainty properly

The denominator is not "42 unmatched." It is confirmed customers whose identifiers could have matched a Meta lead but did not versus confirmed customers for whom Meta-origin identity is fundamentally untestable. Nine of 25 in-window confirmed holds had no recoverable phone; email is complete on the confirmed-hold side. Classify the 42 by observability and put an explicit upper bound on attribution rather than assuming zero or proportional.

B8 — Grep every field on all 9 creatives for { and {{

All nine were bulk-created through a template path that left the creative name as literal {{product.name}}. Harmless in name (renamed since). But nobody has checked whether other template-driven fields carry unresolved braces — url_tags, lead-form hidden/tracking params. Weak corroborating signal: ad_id/adset_id are present on only 326 of 332 leads.

B2 · B6 · B9 · B10 — lower priority, still open

Campaign state

Monthly

PeriodSpendLeadsCPL
Jun 26–30270.51377.31
July1,845.832258.20
Aug 1–16891.687911.29
Last 7d (Aug 9–15)418.142914.42

Caution: "CPL doubled" is an artifact of mismatched bucket lengths. On matched clean 7-day weeks the series is 7.28 · 6.31 · 8.34 · 8.88 · 10.90 · 9.68 · 13.81 — matched 14-day windows give +69%, not ×2.

Per ad set

Ad setSpendCTR7d freqLeadsCPLPartiesCost/party
SQ1 / Mississauga1,003.113.11%1.641725.832502
Hillcrest / Richmond Hill1,001.642.55%1.769111.013334
UCM / Newmarket1,003.271.51%1.947812.860

Spend spread across the three is CA$1.63 — ABO working as designed, so ad-set comparisons are efficiency comparisons, not allocation artifacts. Geo party counts are not significant: Hillcrest vs SQ1 z = 1.21 (p ≈ 0.23); UCM's zero has P(zero) = 0.22 under the null.

CPM × CTR — the fatigue discriminator

Ad setCPM Jun→AugCTR Jun→Aug
SQ114.81 → 11.35 (−23%)4.78% → 2.64%
Hillcrest13.59 → 12.87 (−5%)2.94% → 2.32%
UCM11.84 → 12.26 (+4%)1.65% → 1.30%

CPM flat-to-down while CTR falls ⇒ auction pressure and algorithmic penalty both refuted. But anchoring on the 5-day launch window overstates decay: July→August is −16% / −11% / −18%.

Ad level (lifetime)

AdSetSpendShare of setCTRCPMLeadsCPL
R2 Seamless — UCMUCM911.3390.8%1.53%11.527013.02
R2 Seamless — HILLHILL622.7262.1%2.79%12.80679.29
R2 Seamless — SQ1SQ1509.7450.8%3.18%12.11875.86
R1 4 Reasons — SQ1SQ1483.2348.1%3.03%11.23835.82
R1 4 Reasons — HILLHILL376.8937.6%2.18%12.352415.70
R1 4 Reasons — UCMUCM87.228.7%1.32%11.77810.90
R4 Aya — SQ1SQ111.071.1%4.27%18.8925.54
R4 Aya — UCMUCM5.570.6%1.48%11.780
R4 Aya — HILLHILL2.890.3%1.48%14.240

Per-ad booking outcomes (hub-denominated)

AdLeads (hub)Qualified (milestone)ConfirmedLead→booked
R1 4 Reasons — SQ182111.2%
R2 Seamless — SQ181211.2%
R2 Seamless — UCM66200%
R2 Seamless — HILL66111.5%
R1 4 Reasons — HILL21129.5%
R1 4 Reasons — UCM8000%
R4 Aya — SQ12000%
total32675
Two denominators, do not divide across them. Leads (hub) is the hub-side count (326 of 332 attributed). Every CPL on this page is Meta-denominated (341 campaign-wide). And qualified here is the milestone count (7), not the money-event count (15) — two different definitions.

R1-HILL's 2-of-21 edge is Fisher exact p = 0.143 on three events, and one of many post-hoc splits across 9 ads × 3 geos. It justifies not killing R1. It is not evidence R1 is the booking engine.

Video engagement — and the confound that invalidates the obvious reading

AdImprPlaysPlays/imprp25/imprThruPlay/imprAvg watch
R4 Aya — SQ158653992%15.4%11.09%6s
R4 Aya — HILL20318491%11.8%7.39%4s
R1 4 Reasons — SQ142,99539,89893%8.96%5.65%4s
R1 4 Reasons — HILL30,51028,05492%7.28%4.51%3s
R4 Aya — UCM47342590%5.07%3.17%2s
R1 4 Reasons — UCM7,3916,44787%5.34%3.13%2s
R2 Seamless — SQ142,05913,97133%5.24%1.14%3s
R2 Seamless — UCM79,10025,44732%3.64%0.70%2s
R2 Seamless — HILL48,60911,03223%2.90%0.64%2s
Do not rank creatives on ThruPlay here. reel1 (= R1) is 35.133s; reel2 (= R2) is 16.02s. ThruPlay fires at 15s or 97%, whichever first — so it means 43% watched for R1 and 94% for R2. The comparison ranks the short video last by construction, and it killed an earlier "R2 is the weakest creative" finding.

The clean cross-creative metric is CTR, on which R2 beats R1 in all three ad sets.

video_continuous_2_sec_watched_actions returns NULL for every ad, so a true 3-second hook rate is unavailable. Never substitute video_play_actions — on autoplay it approaches 100% of impressions.

Placement — at position level

Position (30d, all 3 sets)SpendLeadsCPLSQ1UCMHILL
feed789.01849.395.6414.6313.25
facebook_reels220.75258.836.249.6915.74
instagram_stories544.895410.095.9316.7111.89
facebook_stories102.16714.5915.3613.4915.49
instagram_reels129.99621.6714.4628.6443.52

instagram_reels is worst in all three sets; instagram_stories is fine — Hillcrest's best. An aggregate "Facebook beats Instagram" reading is the wrong granularity. Do not exclude it anyway: a placement exclusion is a targeting edit that resets the ad set, shrinks the auction pool, and kills the Feed-format probe — for CA$2.38/day.

Age (30d, campaign)

AgeSpendLeadsCPL
18–246.170
25–34661.79689.73
35–441,089.8910310.58
45–5429.4655.89

The tier, and why lineage cannot see it

TierCountRate
Inquiry (hub)333100%
Qualified — money event154.5%
Qualified — milestone stamped72.1%
Confirmed (lineage)61.8%
Confirmed (contact identity)72.1%
PaidCANNOT RESOLVE — deposit_received_cents populated in 3 of 699 holds (failed control)
Lineage is not broken — it is structurally blind. meta_leads → slot_holds yields 462 rows and exactly ZERO on confirmed. Positive control proves scope: 170 inquiry, 150 lost, 7 cancelled.

Mechanism: the Meta-originated hold always dies lost; the family re-enters through a different door — the website form or a reply to the nurture email — creating a second hold that confirms. Confirmed-hold source surfaces: 39 organic inbound email · 11 main-site form · 2 birthday LP · 0 Meta lead form.

⇒ For Meta booking attribution use contact identity, never lineage.

The seven attributed parties names removed

HoldMatch ruleMeta lead dateSibling Meta holdLag (days)
355phone2026-07-03341 lost4.11
281phone2026-06-28279 lost4.48
344phone2026-06-29290 cancelled4.76
555email local-part (probable)2026-07-18491 inquiry5.00
421phone2026-07-02331 lost8.74
618email2026-07-17479 lost14.37
579phone2026-07-08392 merged18.76

Lag: min 4.11 · median 5.00 · p75 8.74 · max 18.76 days. Every sibling Meta hold is dead — that is the two-door pattern, seven times out of seven.

Cohort conversion + second-door re-entry B11 resolved

GRAIN CORRECTION. The originally-published series (4.24 / 2.04 / 0.00) mixes two grains: 4.24% is 5/118 at lead-event grain including 7 replay rows, while 2.04% is 2/98 at person grain. The clean person-grain series is below. Same shape, different first number.
CohortPeopleMeta-door holdMeta-door died2nd-door re-entryConfirm % (lineage)Confirm % (reconnected)
C1 Jun 27 – Jul 101111111116.31%0.00%3.60% (4)
C2 Jul 11 – Jul 249896964.08%0.00%2.04% (2)
C3 Jul 25 – Aug 77978781.27%0.00%0.00% (0)
C4 Aug 8 – Aug 163837370.00%0.00%0.00% (0)
total326322322 (100%)3.68%0.00%1.84% (6)
THE DECISIVE TEST CAME BACK ON THE BAD SIDE. Second-door re-entry has itself collapsed: 6.31% → 4.08% → 1.27% → 0.00%. Meta-origin people are not migrating to another stage — in the last two cohorts they are not re-entering anywhere at all. That is genuine lead-quality deterioration, monotone on both axes across five weeks.

The stage-migration branch is REFUTED, provably. The lineage-basis confirm rate is 0.00% in every single cohort, so the published series cannot have been computed on a lineage basis — it was already identity-reconnected. Reconnection does not flatten the decline.

And the Meta-originated hold died in 322 of 322 cases — 100%. Not one ever confirmed directly, in any cohort.

CANNOT RESOLVE: the website-origin control comparison. Website-form and nurture-reply prospects have no origin event independent of the hold itself, so a second-door rate for them is not defined the same way. No number was manufactured on an incomparable base.

At the measured median lag of 5.0 days, P(zero August confirms) = 6%, not the 76% an assumed two-week lag implies. The censoring defence does not hold. Note also that five genuine status-kind confirms did occur account-wide in August — none Meta, two Google-LP — and the identity join fires on them, so the Meta zero is not a scope artifact.

Optimization plumbing

All three ad sets run optimization_goal = QUALITY_LEAD — Meta's "Conversion leads", which optimizes for lead quality and requires a CRM lead-status feed. What is actually flowing into dataset 647265948134079 over 7 days: thousands of PageView, ~50 Purchase, and one Lead — against 29 campaign leads. Meanwhile cost_per_result is labelled "Leads (form)".
SignalCount/week/ad-set/weekvs ~50 threshold
Meta leads (the lead event itself)33245.515.23.3× short
birthday_qualified generated, all channels486.62.1923× short
birthday_qualified delivered to Meta182.90.9553× short
birthday_booked generated, all channels354.81.6031× short

The CAPI rail is partly shipped, not absent — 28 events have landed ok, but 29 of 47 qualified attempts were skipped:non_meta_source (see B7). birthday_lead is a Google-only rail (meta_status NULL on all 42). ⇒ CAPI is instrumentation, not a rescue strategy.

Settled — do not re-derive

Substrate traps

Entity IDs

CAMPAIGN   120250641116100494   Birthday — GTA   ACTIVE

AD SETS (all ACTIVE · CA$20.00/day · QUALITY_LEAD · bid "Highest volume")
  SQ1 / Mississauga          120250660577030494
  UCM / Newmarket            120250660577020494
  Hillcrest / Richmond Hill  120250660576970494

ADS (all ACTIVE) — spend · ad_id · adset_id · creative_id
  911.33  120250738811920494  R2-Seamless-UCM    120250660577020494  1789682088862554
  622.72  120250740889250494  R2-Seamless-HILL   120250660576970494  1390539682920319
  509.74  120250739216590494  R2-Seamless-SQ1    120250660577030494  4667070246949676
  483.23  120250738928910494  R1-4Reasons-SQ1    120250660577030494  2084061652519688
  376.89  120250738984910494  R1-4Reasons-HILL   120250660576970494  2228187117956550
   87.22  120250660576980494  R1-4Reasons-UCM    120250660577020494  1269230088423756
   11.07  120250739213690494  R4-Aya-SQ1         120250660577030494  36705252715788517
    5.57  120250660576990494  R4-Aya-UCM         120250660577020494  1723567632218836
    2.89  120250740881430494  R4-Aya-HILL        120250660576970494  2143369303229689

VIDEO ASSETS
  reel1_birthday_9x16_v2.mp4  1388250519892946   35.133s   (= R1)
  reel1_birthday_4x5.mp4      1535628331547571   35.133s
  reel2_birthday_9x16.mp4     1348636140545018   16.020s   (= R2)
  reel2_birthday_4x5.mp4      2159971927913514   16.020s

FIXED REFERENCES
  FB Page             427011523828543
  instagram_user_id   17841462856760171   (hardcode — API re-resolution blocked on this token)
  Instant Form v5     4366657526983768
  Pixel / dataset     647265948134079

NOTE: all 9 creatives were renamed 2026-08-16 from "{{product.name}} 2026-06-28-<hash>"
      to "<concept>-<geo>-2026-06-28". Older documents show the stale names.