Funnel Drop-off Diagnostician
SKILL.md
HOW_TO_USE.md
sample_input.json
expected_output.json
skillsmarketingSKILL.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
# Funnel Drop-off Diagnostician
A drop is only a problem when it is worse than it should be and big enough to matter. Half of all visitors leaving after the first page can be normal; a fifth of checkout starters vanishing can be the most expensive leak in the business. This skill measures every step, decides which drops are real, and ranks them by the conversions they cost.
It reads the data itself. Every number in the report comes from a call below, and the report says which call it came from.
## Before you start
You need the InsightfulPipe MCP connected. GA4 gives the steps on the site. Google Ads, Meta, LinkedIn Ads and Search Console are optional and give the steps above it.
1. Call `query_contexts` with `request="accounts"` for `google-analytics`, and for each of `google-ads`, `facebook-ads`, `linkedin-ads` and `google-search-console` the user wants in the funnel. Note `workspace_id`, `brand_id` and each account id. **Use the accounts of one business only.** If a workspace holds several businesses, ask which one; never put one company's ads on top of another company's site.
2. Call `query_contexts` with `request="actions_details"` for every action below on its platform, and follow the body shapes it returns.
3. Ask the user, and use the defaults when they don't know:
- **The funnel type and its steps.** Default: pick the type from the events that fire (call 2) using the step library below.
- **What one conversion is worth** (an order's margin, a lead's value, a signup's value). Default: none. Impact is then counted in conversions, plus ad spend where there is some.
- **A change date** (a redesign, a new form, a price change). With one, compare equal windows before and after it instead of the prior period.
- **A target rate** for the final step, if they have one.
**Window.** Use the last 30 days ending 2 days before today (GA4 is still processing the most recent days). Call the first day `START` and the last day `END`. The comparison window is the 30 days before `START` (`PRIOR_START` to `PRIOR_END`). Print both windows at the top of the report.
## The step library
Funnels are built from GA4 events and page paths. These are the defaults, using GA4's recommended event names; swap in the user's own.
| Funnel | Steps |
|---|---|
| Lead generation | session โ engaged session โ key page (contact, quote, demo) โ `form_start` โ `form_submit` or `generate_lead` โ `qualify_lead` โ `close_convert_lead` |
| SaaS signup | session โ engaged session โ pricing page โ signup start (the signup page, or the outbound click to the app) โ `sign_up` โ the activation event โ `purchase` |
| Ecommerce | session โ `view_item` โ `add_to_cart` โ `begin_checkout` โ `add_payment_info` โ `purchase` |
| Content and newsletter | session โ engaged session โ `scroll` โ `sign_up` or `generate_lead` |
Above the site, the paid and search steps are: eligible impressions โ impressions โ clicks โ sessions that landed. Below it, revenue: `purchase` value in GA4, conversion value in the ad platform, or a closed-deal event. When a step has no event, write **not tracked** in its row and keep going, and name the event or import that would add it (report item 9).
## How to run the calls
GA4 calls go through `query_data` with `platform="google-analytics"`; other calls name their platform. Add `workspace_id` and `brand_id` from step 1 to each body. Rules the API enforces, all seen on live runs:
- **Request keys are camelCase where GA4 defines them.** Filters go in `dimensionFilter`. After any filtered call, check that the rows match the filter.
- **This is an open funnel.** GA4's Data API counts sessions in which each event happened; it does not know their order, and a session can enter at any step. GA4's ordered (closed) funnel exists only in the Explore screen. So a step rate here is "sessions with step N รท sessions with step Nโ1". When a later step is bigger than the step before it, say so: the earlier step is under-tracked, or people skip it.
- **Count sessions.** Each session has one device, one channel, one country and region, one landing page and one new-or-returning value, so session rows can be added up across those dimensions. Users can't: one person can sit in several rows.
- **An empty result is zero**, not an error. GA4 returns no rows when every metric is 0.
- **Google Ads:** money fields end in `_micros` (divide by 1,000,000); impression share comes back as a fraction (0.27 is 27%); any segment you filter on must also be in `SELECT`. A tap on the call button in an ad counts as a click, so calls from ads sit inside the clicks step.
- **Rate limits.** When a call is refused with "Retry in N seconds", tell the user how long that is, then wait or stop. Don't retry in a loop.
If a call fails, keep going. Mark what it feeds **unknown**, say which call failed and why, and never fill the gap with a guess.
## Part 1: can the funnel be trusted?
**1. Key events configured.**
```json
{"action": "list_key_events", "property_id": "properties/<id>"}
```
**2. Event inventory.** Which steps fire at all.
```json
{"action": "events", "property_id": "properties/<id>",
"dimensions": ["eventName"], "metrics": ["eventCount", "totalUsers", "sessions"],
"start_date": "START", "end_date": "END"}
```
**3. What the property sees.** Hosts other than the site (a staging host, `localhost`) are test traffic.
```json
{"action": "get_report", "property_id": "properties/<id>",
"dimensions": ["hostName"], "metrics": ["sessions"], "start_date": "START", "end_date": "END"}
```
**4. Is tracking steady?** Sessions by week over the window and the comparison window together.
```json
{"action": "get_report", "property_id": "properties/<id>",
"dimensions": ["isoYearIsoWeek"], "metrics": ["sessions", "engagedSessions", "keyEvents"],
"start_date": "PRIOR_START", "end_date": "END"}
```
| Check | Fail when |
|---|---|
| The final step is measured | the goal event never fires in the window (a key event that never fires counts as not measured) |
| Value is measured | warn when the final step carries no revenue or conversion value: impact can then only be counted in conversions, and the steps after it (qualified, closed, paid) are not tracked |
| Tracking is steady | a full week has more than 3 times, or less than a third of, the median of the other weeks. Read rates only inside the steady weeks, and don't compare across the break. |
| Test traffic is kept out | a test host shows up in call 3, or an admin or test page in calls 7 or 9 |
| Steps run in order | a later step has more sessions than the step before it (warn) |
| Ad clicks reach the site | GA4 paid sessions for a platform are under 70% of that platform's clicks (warn; see Part 3) |
| Ad conversions are real leads | an ad platform counts a button tap (a click-to-call or call-or-text button on the site) as a conversion next to confirmed calls or forms (warn: the "lead" step is inflated by up to that action's share). Name and type in call 16 tell you; when unsure, say what to confirm. |
Without GA4, the steady and test-traffic checks are skipped (ad platforms record impressions and clicks themselves) and the click-to-session check is unknown; say so.
A failed or warned check is the first fix in the report, ahead of any drop-off fix, because every rate after it is unreliable. When the final step isn't measured, the funnel is **not graded**.
## Part 2: the site funnel in GA4
**5. Sessions and engagement by segment.** The base of the funnel and of every segment. For a business that serves one country, put `region` in place of `country`.
```json
{"action": "get_report", "property_id": "properties/<id>",
"dimensions": ["deviceCategory", "sessionDefaultChannelGroup", "newVsReturning", "country"],
"metrics": ["sessions", "engagedSessions", "keyEvents", "totalRevenue"],
"start_date": "START", "end_date": "END", "limit": 100000}
```
**6. Sessions with each step, by segment.** Put the funnel's step events in `values`. Summed over all rows, this is the funnel; summed by device, channel, new-or-returning or country (or region, as in call 5), it is that segment's funnel.
```json
{"action": "get_report", "property_id": "properties/<id>",
"dimensions": ["deviceCategory", "sessionDefaultChannelGroup", "newVsReturning", "country", "eventName"],
"metrics": ["sessions"], "start_date": "START", "end_date": "END", "limit": 100000,
"dimensionFilter": {"filter": {"fieldName": "eventName", "inListFilter": {"values": ["session_start", "view_item", "add_to_cart", "begin_checkout", "purchase", "form_start", "form_submit", "generate_lead", "sign_up"]}}}}
```
**7. Page steps.** Sessions that reached the key pages (pricing, contact, signup, demo). Swap in the site's own paths, and drop pages that only mention the word, such as a blog post about pricing.
```json
{"action": "get_report", "property_id": "properties/<id>",
"dimensions": ["pagePath"], "metrics": ["sessions", "totalUsers"],
"start_date": "START", "end_date": "END",
"dimensionFilter": {"orGroup": {"expressions": [
{"filter": {"fieldName": "pagePath", "stringFilter": {"matchType": "CONTAINS", "value": "pricing"}}},
{"filter": {"fieldName": "pagePath", "stringFilter": {"matchType": "CONTAINS", "value": "contact"}}},
{"filter": {"fieldName": "pagePath", "stringFilter": {"matchType": "CONTAINS", "value": "signup"}}},
{"filter": {"fieldName": "pagePath", "stringFilter": {"matchType": "CONTAINS", "value": "demo"}}}]}}}
```
One page's sessions are a step. Several pages can't be added up (one session can see two of them), so a step made of several pages is "at least" its biggest page.
**8. Landing pages.** Where the sessions that leak came in. Put the first step and the goal event in `values`. Rank landing pages by sessions and compare each one's goal rate with the site's.
```json
{"action": "get_report", "property_id": "properties/<id>",
"dimensions": ["landingPage", "eventName"], "metrics": ["sessions"],
"start_date": "START", "end_date": "END", "limit": 100000,
"dimensionFilter": {"filter": {"fieldName": "eventName", "inListFilter": {"values": ["session_start", "form_submit", "generate_lead", "purchase", "sign_up"]}}}}
```
**9. The lead form report.** Starts and submissions per page. A page with form starts and no submissions either loses everyone or doesn't record the submission; check the thank-you step before calling it a leak.
```json
{"action": "get_report", "property_id": "properties/<id>",
"dimensions": ["pagePath", "eventName"], "metrics": ["eventCount", "sessions", "totalUsers"],
"start_date": "START", "end_date": "END",
"dimensionFilter": {"filter": {"fieldName": "eventName", "inListFilter": {"values": ["form_start", "form_submit", "generate_lead"]}}}}
```
**10. Form and funnel parameters.** If a parameter such as `form_id`, `form_name`, `plan_name` or `signup_method` is registered, add it to call 9 as `customEvent:<parameter>` to split the report by form or plan.
```json
{"action": "list_custom_dimensions", "property_id": "properties/<id>"}
```
**11. Value by channel.** Revenue and key events per channel, for the bottom of a full-funnel report.
```json
{"action": "traffic_sources", "property_id": "properties/<id>",
"dimensions": ["sessionDefaultChannelGroup"],
"metrics": ["sessions", "engagedSessions", "keyEvents", "totalRevenue"],
"start_date": "START", "end_date": "END"}
```
**12. The comparison window.** Run calls 5 and 6 again with `PRIOR_START` and `PRIOR_END` (or the before-and-after windows around the change date).
## Part 3: the steps above the site
Use each platform the business advertises on. For a platform it doesn't use, leave the row out.
**Google Ads.** These go through `query_data` with `platform="google-ads"` and `action="gaql"`, with the customer id as `account_id`.
13. Impression share by campaign: the impressions the campaigns were eligible for and lost to budget or to rank.
```sql
SELECT campaign.name, campaign.advertising_channel_type, metrics.impressions,
metrics.search_impression_share, metrics.search_budget_lost_impression_share,
metrics.search_rank_lost_impression_share
FROM campaign
WHERE segments.date BETWEEN 'START' AND 'END' AND metrics.impressions > 0
```
Eligible impressions = impressions รท impression share. Missed to budget = eligible ร budget-lost share; missed to rank = eligible ร rank-lost share. Search campaigns only; other campaign types return no share.
14. The ad funnel by campaign and device.
```sql
SELECT campaign.name, segments.device, metrics.impressions, metrics.clicks,
metrics.cost_micros, metrics.conversions, metrics.conversions_value
FROM campaign
WHERE segments.date BETWEEN 'START' AND 'END' AND metrics.impressions > 0
```
15. What the conversions are. Split the conversions by action, so the lead step shows how leads arrive (calls from the ad, call or text taps on the site, forms).
```sql
SELECT campaign.name, segments.device, segments.conversion_action_name,
segments.conversion_action_category, metrics.conversions, metrics.all_conversions
FROM campaign
WHERE segments.date BETWEEN 'START' AND 'END'
```
16. How each conversion action counts.
```sql
SELECT conversion_action.name, conversion_action.category, conversion_action.type,
conversion_action.primary_for_goal, conversion_action.include_in_conversions_metric,
conversion_action.counting_type, conversion_action.status
FROM conversion_action WHERE conversion_action.status = 'ENABLED'
```
Only actions with `include_in_conversions_metric = true` are in `metrics.conversions`. A page-load action that isn't counted (for example the contact page) shows up only in `all_conversions`; use it as a site step, not as a lead.
17. Landing pages from the ads.
```sql
SELECT landing_page_view.unexpanded_final_url, metrics.clicks, metrics.cost_micros, metrics.conversions
FROM landing_page_view
WHERE segments.date BETWEEN 'START' AND 'END' AND metrics.clicks > 0
```
Read the page path and ignore the tracking template (`{ignore}?campaignid=...`), but keep a templated and a plain version of the same page apart, since they come from different ads. Calls from ads have no landing page, so this report's conversions are fewer than the account's; say so.
18. Where the searchers are: the ad funnel by the searcher's state or province.
```sql
SELECT campaign.name, segments.geo_target_region, metrics.impressions, metrics.clicks,
metrics.cost_micros, metrics.conversions
FROM user_location_view
WHERE segments.date BETWEEN 'START' AND 'END' AND metrics.impressions > 0
```
Each click has one searcher location, so the rows add up to the account's clicks and leads. Compare each region's click-to-lead rate with the rest; regions with under 30 clicks are added together into "other regions" for the test, never graded one by one. Read a campaign that targets other places on purpose (an out-of-state campaign) with that in mind.
19. Region names. Call 18 returns regions as ids (`geoTargetConstants/<id>`); name the ones in the report.
```sql
SELECT geo_target_constant.resource_name, geo_target_constant.name,
geo_target_constant.country_code, geo_target_constant.target_type
FROM geo_target_constant
WHERE geo_target_constant.resource_name IN ('geoTargetConstants/<REGION_ID_1>', 'geoTargetConstants/<REGION_ID_2>')
```
Run calls 13, 14 and 15 again for the comparison window.
**Meta Ads.**
```json
{"platform": "facebook-ads", "action": "get_insights_adaccount", "ad_account_id": "<META_AD_ACCOUNT_ID>",
"level": "account", "time_range": {"since": "START", "until": "END"},
"fields": ["spend", "impressions", "inline_link_clicks", "actions"]}
```
Use `inline_link_clicks` as the click step. In `actions`, count only the action types that match the funnel's goal (such as `lead` or `purchase`); never add every action type together.
**LinkedIn Ads.**
```json
{"platform": "linkedin-ads", "action": "get_account_analytics", "ad_account_id": "<LINKEDIN_AD_ACCOUNT_ID>",
"start_date": "START", "end_date": "END", "time_granularity": "ALL",
"fields": ["impressions", "clicks", "costInUsd", "externalWebsiteConversions", "oneClickLeads"]}
```
Lead-form leads (`oneClickLeads`) never reach the site; show them as their own route.
**Search Console** (the organic steps).
```json
{"platform": "google-search-console", "action": "search_analytics", "site_url": "<GSC_SITE>",
"dimensions": ["date"], "start_date": "START", "end_date": "END", "row_limit": 500}
```
Add up clicks and impressions from the date rows. Search Console lags 2 to 3 days, so count the dates that came back.
**Do the clicks arrive?** Compare each platform's clicks with GA4's sessions for the same channel in call 5 (Google Ads with Paid Search, Search Console with Organic Search, Meta and LinkedIn with Paid Social). Under 70% is a leak between the ad and the page: a slow page, a broken tag, consent banners, or redirects that strip the tracking. Above 100% usually means other sources share the channel.
**When GA4 isn't connected for this business,** run the funnel on the ad platform alone: eligible impressions โ impressions โ clicks โ leads, with the site steps taken from the platform's own page-load and form actions (call 15 `all_conversions`) where they run in order. A page-load action with fewer conversions than the form after it is not a step before the form (the form also sits on other pages); leave it out and say so. Mark the GA4 rows **not connected** and say what they would add.
**Evidence for a landing-page leak (optional).** Slow mobile pages are a common cause of an arrival leak; check the leaking page's speed with Core Web Vitals Fixer (if installed) before blaming the copy. Microsoft Clarity, when connected, can add behaviour signals (dead clicks, rage clicks, quick backs) for the same page through its `query_dashboard` action; that path is untested until a Clarity project is connected.
## Part 4: diagnose
**Step rates.** For every transition: sessions (or impressions, clicks) entering, leaving, the step rate, and the share of the first step still left. Show the funnel as a map:
```
STEP ENTERING STEP RATE LOST
Impressions 12,000
Clicks 840 7.0% 11,160
Leads 210 25.0% 630
Qualified leads not tracked
```
**Is the drop real?** A gap between two rates is real only when it is larger than twice its standard error: for rates p1 and p2 on n1 and n2 entering, the gap must exceed 2 ร โ(p(1โp)(1/n1 + 1/n2)), where p is the pooled rate. Below that, call it noise. Never grade a transition with fewer than 30 entering, and never compare a segment with fewer than 30.
**What to compare with.** Each rate has one reference, and that reference alone sets its severity. The other comparisons are shown next to it as context and never change the grade, even when they disagree.
| Rate | Graded against | Shown as context |
|---|---|---|
| Each transition of the whole funnel (account level) | the same step in the comparison window, when tracking was steady in both | the usual range |
| A campaign's impression share and click rate | the campaign's own comparison window. Both depend on the campaign's keywords, budget and competition (a brand campaign's click rate is naturally several times a generic one's), so they are never graded across campaigns | the other campaigns |
| Click-to-lead and the site steps of a segment (campaign, device, channel, new or returning, country or region, landing page) | everyone else on the same step in the same window. Compare like with like: leave brand campaigns out of the group a non-brand campaign is measured against, and grade a brand campaign against its own comparison window | the segment's own comparison window |
**Rough starting points.** The "low below" line is a floor that always applies, with or without history: a rate under it is at least High, whatever its reference says. The ad lines are for non-brand traffic, so they apply to non-brand campaigns and to account totals, never to a brand campaign. The usual range becomes the reference only when there is no history and no peer group to compare with (a new account, or the first month of tracking). Both are rules of thumb, not benchmarks for your industry; replace them with the business's own numbers as soon as it has 3 months of history.
| Transition | Low below | Usual range |
|---|---|---|
| Session โ engaged session | 40% | 50โ70% |
| Session โ form start (lead gen) | 3% | 5โ20% |
| Form start โ submit | 30% | 40โ65% |
| Session โ signup start (SaaS) | 3% | 5โ15% |
| Signup start โ signup complete | 50% | 60โ85% |
| Product view โ add to cart | 4% | 5โ15% |
| Add to cart โ checkout start | 30% | 40โ65% |
| Checkout start โ purchase | 35% | 45โ70% |
| Search ad impression โ click (non-brand) | 2% | 3โ10% |
| Search ad click โ lead | 4% | 5โ25% |
| Search impression share (non-brand) | 30% | 40โ80% |
**Severity.** For each transition, and each segment of it:
| Severity | When |
|---|---|
| Critical | the rate is under half its reference, and the gap is real |
| High | the rate is 50โ75% of its reference and the gap is real, or it is under the "low below" line (the floor, whatever the reference) |
| Moderate | 75โ90% of its reference with a real gap, or any real gap of at least 5 points |
| Normal | anything else (including gaps that are noise) |
**Impact.** For each leak: the conversions a month it costs = entering ร (reference rate โ current rate) ร the rate from the next step to the final step, scaled to 30 days. Multiply by the value per conversion when the user gave one. Where ads pay for the step, also show the spend that went through the leaking segment and its cost per conversion against the account's. For missed impressions, split them into those lost to budget (the fix is budget) and those lost to rank (the fix is bids or ad quality). Each = eligible impressions ร that lost share ร the campaign's own click rate ร its click-to-conversion rate, stated as a ceiling, because the extra impressions are usually lower quality. Count the budget-lost part only on a campaign whose cost per conversion is at or below the account's; buying more of an expensive campaign is not a fix. The leak's impact is the larger part that counts; name that part as the counted part, and when the budget part was left out although it was the bigger loss, say so.
**High or low impact.** A leak is high impact when it costs at least 5% of the window's conversions, or when its excess spend (spend โ its conversions ร the account's cost per conversion) is at least 10% of the window's spend. Everything else is low impact.
**Causes.** A hypothesis per leak, with the evidence that would confirm it and the call or skill that checks it:
| Stage | Symptom | Likely causes | Evidence |
|---|---|---|---|
| Above the site | impressions fall, share lost to budget | budget caps the best campaign | call 13; a budget move |
| Above the site | low click rate on one campaign | broad match or ad text that doesn't fit the search | search terms, ad copy |
| Arrival | clicks don't become sessions | slow page, tag missing, redirects | the click-to-session check, page speed |
| Arrival | sessions don't engage | the page doesn't match the ad, slow on mobile, wrong audience | landing-page rates (call 8 or 17), device split, page speed |
| Consideration | key page seen, no form start or add to cart | value unclear, price shock, no proof | page steps (call 7), pricing page |
| Action | form starts, few submits | too many fields, errors, a required phone number, no reassurance | lead form report (call 9) |
| Checkout | checkout starts, few purchases | surprise costs, forced account, few payment options | checkout steps by device |
| After conversion | leads that never qualify, signups that never activate | wrong promise in the ad or page, slow follow-up | qualify and activation events |
A hypothesis is never presented as the cause. Say what would prove it: a session recording, a form-field report, a test.
**Fixes to try, by stage.** Pick from these only after the evidence points at the stage:
| Stage | Fixes to try |
|---|---|
| Above the site | move budget to the campaigns with the cheapest conversions; tighten match types and add negatives from real search terms; ad text that repeats the searcher's words |
| Arrival | a landing page per intent instead of the home page; a faster mobile page; a headline that answers the ad |
| Consideration | lead with the outcome and proof (results, reviews, guarantees); show a price or a starting price with what's included; a comparison page |
| Action | fewer form fields; an optional phone number; errors shown next to the field; a progress bar on multi-step forms; say what happens after submitting |
| Checkout | all costs shown before checkout; guest checkout; wallets and more payment options; a reminder for abandoned carts |
| After conversion | same-day follow-up for leads; a quick first win in onboarding; a clear trigger to upgrade |
**Funnel health score.** Start at 100. Take off 20 for each critical transition, 10 for each high and 4 for each moderate. Each transition counts once, at the worse of its account-level severity and its worst segment's. Grade: 85โ100 A (well tuned), 70โ84 B (healthy, clear wins), 55โ69 C (leaks are costing conversions), 40โ54 D (several stages broken), under 40 F (fix now). **Not graded** when the final step isn't measured or, on the GA4 path, tracking isn't steady across the window; say which.
## Report format
1. **Summary:** funnel type, windows, source per step, entering and converting volume, the overall rate, the score and grade (or "not graded" and why), and in one line each: the biggest leak, its likely cause, the first fix.
2. **The funnel map** with every step, including the "not tracked" ones.
3. **Transition table:** one row per transition of the whole funnel, labelled account level: entering, rate, comparison-window rate, reference, real or noise and the account-level severity, plus the worst segment severity on that transition. The score counts the worse of the two.
4. **Segments:** device, channel, new or returning, country or region, campaign and landing page, only where the gap is real or the segment is large; say "no real gap" where there is none.
5. **Priority matrix:** every leak found gets a fix, and each fix is placed by impact (high or low, as defined above) and effort (low, medium, high): do first (high impact, low or medium effort), plan next (high impact, high effort), quick wins (low impact, low effort), later (the rest). Trust fixes always go in "do first".
6. **Top 5 fixes:** trust fixes first, then by conversions a month. Each with the step, the current and target rate, the impact, the cause hypothesis and its evidence, the fix, effort, and the skill to go deeper.
7. **Tests:** for the top 3 leaks, a test written as "if we change X, step Y's rate rises from A to B, because C". For a page or form leak, give the sessions (or clicks) each version needs to detect that lift (95% confidence, 80% power: 7.85 ร (p1(1โp1) + p2(1โp2)) รท (p2 โ p1)ยฒ) and how many months that takes at today's volume; when it is more than 2 months, say so and suggest a before-and-after comparison with the same gap test instead. For a budget leak, a stepped budget test with a stop rule (stop when the cost per conversion passes the account's).
8. **Monitoring:** for each step, the current rate, a warning line (the comparison window's rate minus its 2-standard-error band) and a critical line (half the reference), checked weekly. Add the mobile-versus-desktop gap on the conversion step, with the gap that would count as real at today's volume as its warning line.
9. **What couldn't be checked,** and why. For each step marked not tracked, name the event or import that would add it (for example `generate_lead`, `qualify_lead`, or an offline import of signed deals into the ad platform).
## Design a funnel
When the user has no funnel yet, or asks for funnel ideas, map one before diagnosing:
| Stage | The question it answers | What moves people on | How it's measured |
|---|---|---|---|
| Awareness | Do the right people find us? | search, social, partner and ad reach | impressions, Search Console impressions |
| Interest | Do they want to know more? | a page that matches the search or the ad | engaged sessions, scroll |
| Consideration | Do they believe it fits them? | proof, comparisons, pricing, a demo or sample | key page views, return visits |
| Conversion | Do they act? | one clear action with little effort | the key event |
| Retention and referral | Do they stay and bring others? | onboarding, follow-up email, referral offers | repeat purchase, activation, referrals |
Use the data the skill pulled: which channels already bring engaged sessions (call 11), which pages already lead to action (call 8), and which stages have no event yet. For each stage give two or three ideas that fit the business, the event that measures it, and the target to beat next month. Ideas to draw on: a lead magnet or quiz for interest, a comparison or calculator page for consideration, a shorter form or a calendar booking for conversion, a remarketing audience built from the drop-off step, a welcome or onboarding sequence for retention.
## Fixes this skill can run
Only after the user confirms each one, with the exact payload shown first. Both need a Read & Write connection, and an owner, admin or operator can switch them off. It never changes the site, the ads, tags or forms; those it writes up as recommendations.
**Mark the goal as a key event,** when the final step fires but isn't a key event (call 1):
```json
{"action": "create_key_event", "property_id": "properties/<id>",
"event_name": "generate_lead", "counting_method": "ONCE_PER_SESSION"}
```
**Build a drop-off audience** for remarketing, for example people who started the form in a session and didn't submit it in that session. First list the existing audiences, so the skill never creates one that already does the same job:
```json
{"action": "list_audiences", "property_id": "properties/<id>"}
```
Then, after the user confirms (`membership_duration_days` can't be changed later):
```json
{"action": "create_audience", "property_id": "properties/<id>",
"display_name": "Form started, not submitted (30 days)",
"description": "Started the lead form and did not submit it in the same session",
"membership_duration_days": 30,
"filter_clauses": [
{"clauseType": "INCLUDE", "simpleFilter": {"scope": "AUDIENCE_FILTER_SCOPE_WITHIN_SAME_SESSION",
"filterExpression": {"andGroup": {"filterExpressions": [{"orGroup": {"filterExpressions": [
{"eventFilter": {"eventName": "form_start"}}]}}]}}}},
{"clauseType": "EXCLUDE", "simpleFilter": {"scope": "AUDIENCE_FILTER_SCOPE_WITHIN_SAME_SESSION",
"filterExpression": {"andGroup": {"filterExpressions": [{"orGroup": {"filterExpressions": [
{"eventFilter": {"eventName": "form_submit"}}]}}]}}}}]}
```
The same shape builds "added to cart, no purchase" or "saw pricing, no signup" (swap the events).
Offer them only when they apply: no key event for a goal that never fires, and no audience for a step with under 100 sessions a month.
## Without a connection
If the user hasn't connected GA4 or an ad platform, ask for each step's name and count (sessions or users), ideally by device and source, for the window and the one before it. Run Parts 1 and 4 on those numbers, mark every figure "from your data, not checked", and say which calls above would replace them once connected.
## Rules
- One business per funnel. Never mix accounts of different companies.
- Trust fixes come before drop-off fixes. A funnel whose final step isn't measured is not graded.
- Every rate shows its entering count. Nothing with fewer than 30 entering is graded (show it as "not graded"), no segment comparison on fewer than 30 per side, and no gap called real without the test.
- Open funnel, stated: steps are sessions with each event, not ordered paths.
- Never add users across rows, and never add up several pages as one step.
- Count conversions only from the actions the platform counts, and say how leads arrive (call, form, tap).
- Impact is in conversions a month, and in money only with a value the user gave or the platform's own value. Ad spend is shown as spend and excess spend, never as lost revenue.
- A cause is a hypothesis with its evidence, never a fact.
- Write actions only after confirmation, one at a time.
## Go deeper
- Page-level fixes for a leaking landing page: Landing Page Auditor; the form itself: Form CRO; signup steps: Signup Flow CRO; after signup: Onboarding CRO.
- The ad-to-page match behind an arrival leak: Ad Landing Page Match Scorer.
- Checkout and product steps for a store: GA4 Ecommerce Funnel (if installed).
- Budget moves for impressions lost to budget: Budget Allocation Optimizer.
- Tests for the top leaks: A/B Test Designer.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# How to Use This Skill
Hey Claude, I just added the "funnel-dropoff-diagnostician" skill. Build our funnel for the last 30 days, compare it with the 30 days before, and tell me where we lose the most people and what to fix first.
## What you need
- **The InsightfulPipe MCP connected to Claude, with your GA4 property.** The skill pulls the events, pages and sessions for each step itself; you don't export anything.
- **Your ad accounts and Search Console (optional):** Google Ads, Meta Ads, LinkedIn Ads and Search Console add the steps above the site, from impression to click. Use the accounts of one business only.
- **Your funnel steps, if you have them.** Without them it picks a funnel from the events that fire, using GA4's recommended names (`form_start`, `generate_lead`, `sign_up`, `add_to_cart`, `begin_checkout`, `purchase`).
- **What one conversion is worth (optional),** so leaks can be priced in money, not only in conversions.
- **The date of any recent change (optional),** such as a redesign or a new form, for a before-and-after comparison.
## Example requests
**Where are we losing people?**
Hey Claude, I just added the "funnel-dropoff-diagnostician" skill. Our traffic is fine but few visitors buy. Find the step that loses the most people and what it costs us each month.
**Full funnel, impression to revenue**
Hey Claude, I just added the "funnel-dropoff-diagnostician" skill. Build our full funnel from ad impression to lead and revenue across Google Ads, Meta and GA4, and show where each channel leaks.
**SaaS signup funnel**
Hey Claude, I just added the "funnel-dropoff-diagnostician" skill. Report on our signup funnel: visit, pricing page, signup started, signup completed and activated. Is any step worse than last month?
**Lead form report**
Hey Claude, I just added the "funnel-dropoff-diagnostician" skill. Which pages get form starts but few submissions, and is mobile worse than desktop?
**Before and after a redesign**
Hey Claude, I just added the "funnel-dropoff-diagnostician" skill. We changed the checkout on September 15. Compare the 3 weeks before and after, step by step. Did it help or hurt?
**Design a funnel**
Hey Claude, I just added the "funnel-dropoff-diagnostician" skill. We don't have a real funnel yet. Map one for our business from first visit to repeat customer, with ideas for each stage and the event that should measure it.
## What you get
1. **A summary:** the funnel, the windows, the source for each step, the overall rate, a health score and grade (or "not graded" and why), the biggest leak, its likely cause and the first fix.
2. **A trust check** before any diagnosis: is the goal measured, is tracking steady, do ad clicks reach the site, are the "leads" real leads. A failed check comes first in the fixes.
3. **The funnel map** from impression to conversion, with "not tracked" where the data stops.
4. **Each step against the prior period,** with a test that separates real drops from noise.
5. **Segments:** devices, channels, new or returning visitors, countries or regions, campaigns and landing pages, only where the gap is real.
6. **A priority matrix and the top 5 fixes,** each with its cost in conversions a month (and money, when a value is known), the likely cause, the evidence and the skill to go deeper.
7. **Tests** for the top leaks, with how much traffic each needs, and **alert lines** for each step and for the mobile-versus-desktop gap, to watch weekly.
8. **What couldn't be checked,** and why.
## What it can change for you
With your yes on the exact change, it can:
- mark your goal event (for example `generate_lead`) as a GA4 key event, when it fires but isn't one;
- create a GA4 audience of people who dropped off at a step (for example "started the form, didn't submit"), for remarketing.
These need a Read & Write connection, and an owner, admin or operator can switch them off. It never changes your site, ads, tags or forms; those it writes up as recommendations.
## Tips
- Give it at least 30 days. Steps with fewer than 30 sessions or clicks are shown but not graded.
- If a step has no event yet, the report says so and lists the event to add, so next month's run can see it.
- Tell it about recent changes (prices, forms, budgets, tracking) so it doesn't mistake a tracking break for a real drop.
- Without a connection, paste each step's counts for this period and the one before; it runs the same analysis and marks those figures "from your data, not checked".
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
{
"request": "Hey Claude, I just added the \"funnel-dropoff-diagnostician\" skill. Build our full funnel from ad impression to lead for the last 30 days, compare it with the 30 days before, and tell me where we lose the most people and what to fix first.",
"account": "Example Home Services, a heating, cooling and plumbing company (invented example)",
"platform": "Google Ads (Search campaigns)",
"window": {
"start": "2026-09-20",
"end": "2026-10-19",
"comparison_start": "2026-08-21",
"comparison_end": "2026-09-19"
},
"funnel": "Lead generation: eligible impressions โ impressions โ clicks โ leads (calls, tap to call, booking form, chat)",
"value_per_lead": "not given",
"change_date": "none",
"data_pulled": [
{
"call": "13",
"what": "impression share by campaign",
"rows": 8,
"rows_comparison_window": 8
},
{
"call": "14",
"what": "ad funnel by campaign and device",
"rows": 24,
"rows_comparison_window": 24
},
{
"call": "15",
"what": "conversions by action, campaign and device",
"rows": 47,
"rows_comparison_window": 47
},
{
"call": "16",
"what": "conversion action settings",
"rows": 16,
"rows_comparison_window": null
},
{
"call": "17",
"what": "ad landing pages",
"rows": 9,
"rows_comparison_window": null
},
{
"call": "18",
"what": "ad funnel by the searcher's region",
"rows": 11,
"rows_comparison_window": null
},
{
"call": "19",
"what": "region names",
"rows": 5,
"rows_comparison_window": null
}
],
"data": {
"campaigns_current": {
"Brand": {
"impressions": 4200,
"clicks": 820,
"cost": 1450,
"leads": 96,
"impression_share": 0.86,
"lost_to_budget": 0,
"lost_to_rank": 0.14
},
"AC Repair": {
"impressions": 21000,
"clicks": 1050,
"cost": 6300,
"leads": 126,
"impression_share": 0.52,
"lost_to_budget": 0.31,
"lost_to_rank": 0.17
},
"Emergency Plumbing": {
"impressions": 9800,
"clicks": 640,
"cost": 4300,
"leads": 83,
"impression_share": 0.55,
"lost_to_budget": 0.27,
"lost_to_rank": 0.18
},
"Water Heaters": {
"impressions": 8600,
"clicks": 470,
"cost": 3100,
"leads": 52,
"impression_share": 0.66,
"lost_to_budget": 0.05,
"lost_to_rank": 0.29
},
"Furnace Repair": {
"impressions": 6400,
"clicks": 330,
"cost": 2200,
"leads": 33,
"impression_share": 0.61,
"lost_to_budget": 0.04,
"lost_to_rank": 0.35
},
"Drain Cleaning": {
"impressions": 7200,
"clicks": 400,
"cost": 2600,
"leads": 12,
"impression_share": 0.63,
"lost_to_budget": 0.02,
"lost_to_rank": 0.35
},
"HVAC Installation": {
"impressions": 3100,
"clicks": 120,
"cost": 1900,
"leads": 9,
"impression_share": 0.38,
"lost_to_budget": 0,
"lost_to_rank": 0.62
},
"Out of Area": {
"impressions": 1500,
"clicks": 60,
"cost": 330,
"leads": 4,
"impression_share": 0.4,
"lost_to_budget": 0,
"lost_to_rank": 0.6
}
},
"campaigns_comparison": {
"Brand": {
"impressions": 4350,
"clicks": 850,
"cost": 1420,
"leads": 101,
"impression_share": 0.93,
"lost_to_budget": 0,
"lost_to_rank": 0.07
},
"AC Repair": {
"impressions": 26500,
"clicks": 1300,
"cost": 6250,
"leads": 158,
"impression_share": 0.71,
"lost_to_budget": 0.12,
"lost_to_rank": 0.17
},
"Emergency Plumbing": {
"impressions": 11800,
"clicks": 760,
"cost": 4400,
"leads": 99,
"impression_share": 0.68,
"lost_to_budget": 0.14,
"lost_to_rank": 0.18
},
"Water Heaters": {
"impressions": 8700,
"clicks": 460,
"cost": 3050,
"leads": 51,
"impression_share": 0.67,
"lost_to_budget": 0.05,
"lost_to_rank": 0.28
},
"Furnace Repair": {
"impressions": 6200,
"clicks": 300,
"cost": 2000,
"leads": 30,
"impression_share": 0.6,
"lost_to_budget": 0.04,
"lost_to_rank": 0.36
},
"Drain Cleaning": {
"impressions": 7400,
"clicks": 310,
"cost": 2000,
"leads": 11,
"impression_share": 0.64,
"lost_to_budget": 0.02,
"lost_to_rank": 0.34
},
"HVAC Installation": {
"impressions": 3300,
"clicks": 130,
"cost": 2000,
"leads": 10,
"impression_share": 0.45,
"lost_to_budget": 0,
"lost_to_rank": 0.55
},
"Out of Area": {
"impressions": 1550,
"clicks": 70,
"cost": 360,
"leads": 5,
"impression_share": 0.41,
"lost_to_budget": 0,
"lost_to_rank": 0.59
}
},
"split_by_device": {
"MOBILE": {
"impressions_share": 0.8,
"clicks_share": 0.8,
"lead_rate_factor": 1
},
"DESKTOP": {
"impressions_share": 0.17,
"clicks_share": 0.16,
"lead_rate_factor": 1.1
},
"TABLET": {
"impressions_share": 0.03,
"clicks_share": 0.04,
"lead_rate_factor": 0.55
}
},
"split_by_conversion_action": {
"Calls from ads": 0.22,
"Booking form": 0.3,
"Tap to call (website)": 0.34,
"Live chat": 0.09,
"Phone call (offline import)": 0.05
},
"landing_pages": [
{
"page": "https://www.example.com/",
"clicks": 820,
"leads": 75,
"cost": 1450
},
{
"page": "https://www.example.com/ac-repair/",
"clicks": 1050,
"leads": 98,
"cost": 6300
},
{
"page": "https://www.example.com/emergency-plumber/",
"clicks": 618,
"leads": 62,
"cost": 4152
},
{
"page": "https://www.example.com/free-estimate/",
"clicks": 22,
"leads": 3,
"cost": 147
},
{
"page": "https://www.example.com/water-heater-repair/",
"clicks": 470,
"leads": 41,
"cost": 3100
},
{
"page": "https://www.example.com/furnace-repair/",
"clicks": 330,
"leads": 26,
"cost": 2200
},
{
"page": "https://www.example.com/drain-cleaning/",
"clicks": 400,
"leads": 9,
"cost": 2600
},
{
"page": "https://www.example.com/hvac-installation/",
"clicks": 120,
"leads": 7,
"cost": 1900
},
{
"page": "https://www.example.com/service-area/",
"clicks": 60,
"leads": 3,
"cost": 330
}
],
"regions": [
{
"region": "region-1",
"campaign": "Brand",
"clicks": 820,
"leads": 96
},
{
"region": "region-1",
"campaign": "AC Repair",
"clicks": 1050,
"leads": 126
},
{
"region": "region-1",
"campaign": "Emergency Plumbing",
"clicks": 640,
"leads": 83
},
{
"region": "region-1",
"campaign": "Water Heaters",
"clicks": 470,
"leads": 52
},
{
"region": "region-1",
"campaign": "Furnace Repair",
"clicks": 330,
"leads": 33
},
{
"region": "region-1",
"campaign": "Drain Cleaning",
"clicks": 400,
"leads": 12
},
{
"region": "region-1",
"campaign": "HVAC Installation",
"clicks": 120,
"leads": 9
},
{
"region": "region-2",
"campaign": "Out of Area",
"clicks": 24,
"leads": 1.6
},
{
"region": "region-3",
"campaign": "Out of Area",
"clicks": 18,
"leads": 1.2
},
{
"region": "region-4",
"campaign": "Out of Area",
"clicks": 12,
"leads": 0.8
},
{
"region": "region-5",
"campaign": "Out of Area",
"clicks": 6,
"leads": 0.4
}
],
"split_rule": "Device, action and region rows are the campaign totals split by these shares; the parts add up to the totals."
},
"note": "Example data: an invented heating, cooling and plumbing company with an invented Google Ads account, not a real business. Every number in expected_output.json follows from this input."
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
{
"skill": "funnel-dropoff-diagnostician",
"account": "Example Home Services, a heating, cooling and plumbing company (invented example)",
"summary": {
"funnel": "Lead generation, ad impression to lead (Google Ads Search)",
"window": "2026-09-20 to 2026-10-19",
"comparison_window": "2026-08-21 to 2026-09-19",
"sources": "Google Ads for every step; GA4 not connected for this business, so the site steps are taken from the account's own conversion actions",
"eligible_impressions": 109945,
"impressions": 61800,
"clicks": 3890,
"leads": 415,
"impression_to_lead_pct": 0.67,
"spend_usd": 22180,
"cost_per_lead_usd": 53,
"change_vs_comparison_window": {
"impressions_pct": -11.5,
"clicks_pct": -6.9,
"leads_pct": -10.8
},
"score": 70,
"grade": "B",
"biggest_leak": "AC Repair campaign: 52.0% impression share (71.0% in the comparison window), up to 75 leads a month as a ceiling",
"likely_cause": "Budget runs out on the two cheapest non-brand campaigns, so the top of the funnel shrank while the click-to-lead rate held.",
"first_fix": "Check that the 'Tap to call' action counts real calls, then move budget from Drain Cleaning into AC Repair and Emergency Plumbing."
},
"trust_checks": [
{
"check": "The final step is measured",
"result": "pass",
"detail": "5 enabled conversion actions are counted; they recorded 415 leads in the window."
},
{
"check": "Value is measured",
"result": "warn",
"detail": "Conversion value is 0 on every campaign, and no qualified-lead or booked-job step is imported. The funnel ends at leads, and impact is counted in leads and spend."
},
{
"check": "Ad conversions are real leads",
"result": "warn",
"detail": "'Tap to call (website)' is a counted website action and recorded 142 of the 415 leads (34.2%). Its name and type suggest a tap on a call button, not a completed call. If so, the lead step is inflated by up to that share."
},
{
"check": "Steps run in order",
"result": "warn",
"detail": "The booking-page load action recorded 276 visits, but the booking form recorded 124 leads. Bookings are also submitted on landing pages without a visit to the booking page, so the page load is not a step before the form and is left out of the funnel."
},
{
"check": "Tracking is steady / test traffic",
"result": "skipped",
"detail": "GA4 is not connected for this business; the ad platform records impressions and clicks itself."
},
{
"check": "Ad clicks reach the site",
"result": "unknown",
"detail": "Needs GA4 Paid Search sessions to compare with the clicks; GA4 is not connected for this business."
}
],
"funnel_map": [
{
"step": "Eligible impressions (Search)",
"count": 109945,
"comparison_window": 105349,
"source": "Google Ads call 13 (impressions รท impression share)"
},
{
"step": "Impressions",
"count": 61800,
"comparison_window": 69800,
"step_rate_pct": 56.2,
"lost": 48145,
"lost_split": {
"to_budget": 18630,
"to_rank": 29515
},
"source": "Google Ads call 14"
},
{
"step": "Clicks",
"count": 3890,
"comparison_window": 4180,
"step_rate_pct": 6.3,
"lost": 57910,
"source": "Google Ads call 14"
},
{
"step": "Sessions that landed",
"count": "not connected",
"source": "GA4 is not connected for this business"
},
{
"step": "Leads (counted conversions)",
"count": 415,
"comparison_window": 465,
"step_rate_pct": 10.7,
"share_of_impressions_pct": 0.67,
"source": "Google Ads calls 14 and 15"
},
{
"step": "Booked jobs",
"count": "not tracked",
"source": "no offline import or value"
}
],
"lead_routes": [
{
"route": "Tap to call (website)",
"leads": 142,
"share_pct": 34.2,
"comparison_window_leads": 158
},
{
"route": "Booking form",
"leads": 124,
"share_pct": 29.9,
"comparison_window_leads": 139
},
{
"route": "Calls from ads",
"leads": 91,
"share_pct": 21.9,
"comparison_window_leads": 102
},
{
"route": "Live chat",
"leads": 37,
"share_pct": 8.9,
"comparison_window_leads": 42
},
{
"route": "Phone call (offline import)",
"leads": 21,
"share_pct": 5.1,
"comparison_window_leads": 24
}
],
"lead_routes_note": "Calls from ads happen on the results page and never reach the site; the other routes start with a site visit.",
"counted_conversion_actions": [
"Booking form",
"Calls from ads",
"Live chat",
"Phone call (offline import)",
"Tap to call (website)"
],
"transitions": [
{
"transition": "Eligible impressions โ impressions (impression share)",
"level": "account",
"entering": 109945,
"entering_unit": "eligible impressions",
"rate_pct": 56.2,
"comparison_window_rate_pct": 66.3,
"reference": "comparison window",
"low_below_pct": 30,
"gap_test": {
"gap_points": -10,
"threshold_points": 0.4,
"verdict": "real"
},
"severity": "moderate",
"worst_segment_severity": "high",
"severity_counted_in_score": "high"
},
{
"transition": "Impressions โ clicks",
"level": "account",
"entering": 61800,
"entering_unit": "impressions",
"rate_pct": 6.3,
"comparison_window_rate_pct": 6,
"reference": "comparison window",
"low_below_pct": 2,
"gap_test": {
"gap_points": 0.3,
"threshold_points": 0.3,
"verdict": "real"
},
"severity": "normal",
"worst_segment_severity": "normal",
"severity_counted_in_score": "normal"
},
{
"transition": "Clicks โ leads",
"level": "account",
"entering": 3890,
"entering_unit": "clicks",
"rate_pct": 10.7,
"comparison_window_rate_pct": 11.1,
"reference": "comparison window",
"low_below_pct": 4,
"gap_test": {
"gap_points": -0.5,
"threshold_points": 1.4,
"verdict": "noise"
},
"severity": "normal",
"worst_segment_severity": "critical",
"severity_counted_in_score": "critical"
}
],
"severity_by_transition": {
"Eligible impressions โ impressions": "high",
"Impressions โ clicks": "normal",
"Clicks โ leads": "critical"
},
"score_working": "100 - 10 x 1 high - 20 x 1 critical - 4 x 0 moderate = 70",
"segments": {
"campaigns": [
{
"campaign": "AC Repair",
"impressions": 21000,
"clicks": 1050,
"leads": 126,
"spend_usd": 6300,
"cost_per_lead_usd": 50,
"ctr_pct": 5,
"click_to_lead_pct": 12,
"impression_share_pct": 52,
"lost_to_budget_pct": 31,
"lost_to_rank_pct": 17,
"impression_share_prior_pct": 71,
"impression_share_test": {
"gap_points": -19,
"threshold_points": 0.7,
"verdict": "real"
},
"impression_share_severity": "high",
"ctr_prior_pct": 4.9,
"ctr_severity": "normal",
"click_to_lead_reference": {
"against": "the other non-brand campaigns",
"rate_pct": 9.6,
"gap_test": {
"gap_points": 2.4,
"threshold_points": 2.3,
"verdict": "real"
}
},
"click_to_lead_severity": "normal",
"click_to_lead_vs_own_window": {
"rate_pct": 12.2,
"gap_test": {
"gap_points": -0.2,
"threshold_points": 2.7,
"verdict": "noise"
},
"reading": "worse (noise)"
}
},
{
"campaign": "Emergency Plumbing",
"impressions": 9800,
"clicks": 640,
"leads": 83,
"spend_usd": 4300,
"cost_per_lead_usd": 52,
"ctr_pct": 6.5,
"click_to_lead_pct": 13,
"impression_share_pct": 55,
"lost_to_budget_pct": 27,
"lost_to_rank_pct": 18,
"impression_share_prior_pct": 68,
"impression_share_test": {
"gap_points": -13,
"threshold_points": 1,
"verdict": "real"
},
"impression_share_severity": "moderate",
"ctr_prior_pct": 6.4,
"ctr_severity": "normal",
"click_to_lead_reference": {
"against": "the other non-brand campaigns",
"rate_pct": 9.7,
"gap_test": {
"gap_points": 3.3,
"threshold_points": 2.7,
"verdict": "real"
}
},
"click_to_lead_severity": "normal",
"click_to_lead_vs_own_window": {
"rate_pct": 13,
"gap_test": {
"gap_points": -0.1,
"threshold_points": 3.6,
"verdict": "noise"
},
"reading": "worse (noise)"
}
},
{
"campaign": "Water Heaters",
"impressions": 8600,
"clicks": 470,
"leads": 52,
"spend_usd": 3100,
"cost_per_lead_usd": 60,
"ctr_pct": 5.5,
"click_to_lead_pct": 11.1,
"impression_share_pct": 66,
"lost_to_budget_pct": 5,
"lost_to_rank_pct": 29,
"impression_share_prior_pct": 67,
"impression_share_test": {
"gap_points": -1,
"threshold_points": 1.2,
"verdict": "noise"
},
"impression_share_severity": "normal",
"ctr_prior_pct": 5.3,
"ctr_severity": "normal",
"click_to_lead_reference": {
"against": "the other non-brand campaigns",
"rate_pct": 10.3,
"gap_test": {
"gap_points": 0.8,
"threshold_points": 3.1,
"verdict": "noise"
}
},
"click_to_lead_severity": "normal",
"click_to_lead_vs_own_window": {
"rate_pct": 11.1,
"gap_test": {
"gap_points": 0,
"threshold_points": 4.1,
"verdict": "noise"
},
"reading": "worse (noise)"
}
},
{
"campaign": "Drain Cleaning",
"impressions": 7200,
"clicks": 400,
"leads": 12,
"spend_usd": 2600,
"cost_per_lead_usd": 217,
"ctr_pct": 5.6,
"click_to_lead_pct": 3,
"impression_share_pct": 63,
"lost_to_budget_pct": 2,
"lost_to_rank_pct": 35,
"impression_share_prior_pct": 64,
"impression_share_test": {
"gap_points": -1,
"threshold_points": 1.3,
"verdict": "noise"
},
"impression_share_severity": "normal",
"ctr_prior_pct": 4.2,
"ctr_severity": "normal",
"click_to_lead_reference": {
"against": "the other non-brand campaigns",
"rate_pct": 11.5,
"gap_test": {
"gap_points": -8.5,
"threshold_points": 3.3,
"verdict": "real"
}
},
"click_to_lead_severity": "critical",
"click_to_lead_vs_own_window": {
"rate_pct": 3.5,
"gap_test": {
"gap_points": -0.5,
"threshold_points": 2.7,
"verdict": "noise"
},
"reading": "worse (noise)"
}
},
{
"campaign": "Furnace Repair",
"impressions": 6400,
"clicks": 330,
"leads": 33,
"spend_usd": 2200,
"cost_per_lead_usd": 67,
"ctr_pct": 5.2,
"click_to_lead_pct": 10,
"impression_share_pct": 61,
"lost_to_budget_pct": 4,
"lost_to_rank_pct": 35,
"impression_share_prior_pct": 60,
"impression_share_test": {
"gap_points": 1,
"threshold_points": 1.4,
"verdict": "noise"
},
"impression_share_severity": "normal",
"ctr_prior_pct": 4.8,
"ctr_severity": "normal",
"click_to_lead_reference": {
"against": "the other non-brand campaigns",
"rate_pct": 10.4,
"gap_test": {
"gap_points": -0.4,
"threshold_points": 3.6,
"verdict": "noise"
}
},
"click_to_lead_severity": "normal",
"click_to_lead_vs_own_window": {
"rate_pct": 10,
"gap_test": {
"gap_points": 0,
"threshold_points": 4.8,
"verdict": "noise"
},
"reading": "worse (noise)"
}
},
{
"campaign": "HVAC Installation",
"impressions": 3100,
"clicks": 120,
"leads": 9,
"spend_usd": 1900,
"cost_per_lead_usd": 211,
"ctr_pct": 3.9,
"click_to_lead_pct": 7.5,
"impression_share_pct": 38,
"lost_to_budget_pct": 0,
"lost_to_rank_pct": 62,
"impression_share_prior_pct": 45,
"impression_share_test": {
"gap_points": -7,
"threshold_points": 1.6,
"verdict": "real"
},
"impression_share_severity": "moderate",
"ctr_prior_pct": 3.9,
"ctr_severity": "normal",
"click_to_lead_reference": {
"against": "the other non-brand campaigns",
"rate_pct": 10.5,
"gap_test": {
"gap_points": -3,
"threshold_points": 5.7,
"verdict": "noise"
}
},
"click_to_lead_severity": "normal",
"click_to_lead_vs_own_window": {
"rate_pct": 7.7,
"gap_test": {
"gap_points": -0.2,
"threshold_points": 6.7,
"verdict": "noise"
},
"reading": "worse (noise)"
}
},
{
"campaign": "Brand",
"impressions": 4200,
"clicks": 820,
"leads": 96,
"spend_usd": 1450,
"cost_per_lead_usd": 15,
"ctr_pct": 19.5,
"click_to_lead_pct": 11.7,
"impression_share_pct": 86,
"lost_to_budget_pct": 0,
"lost_to_rank_pct": 14,
"impression_share_prior_pct": 93,
"impression_share_test": {
"gap_points": -7,
"threshold_points": 1.3,
"verdict": "real"
},
"impression_share_severity": "moderate",
"ctr_prior_pct": 19.5,
"ctr_severity": "normal",
"click_to_lead_reference": {
"against": "own comparison window",
"rate_pct": 11.9,
"gap_test": {
"gap_points": -0.2,
"threshold_points": 3.2,
"verdict": "noise"
}
},
"click_to_lead_severity": "normal"
},
{
"campaign": "Out of Area",
"impressions": 1500,
"clicks": 60,
"leads": 4,
"spend_usd": 330,
"cost_per_lead_usd": 82,
"ctr_pct": 4,
"click_to_lead_pct": 6.7,
"impression_share_pct": 40,
"lost_to_budget_pct": 0,
"lost_to_rank_pct": 60,
"impression_share_prior_pct": 41,
"impression_share_test": {
"gap_points": -1,
"threshold_points": 2.3,
"verdict": "noise"
},
"impression_share_severity": "normal",
"ctr_prior_pct": 4.5,
"ctr_severity": "normal",
"click_to_lead_reference": {
"against": "the other non-brand campaigns",
"rate_pct": 10.5,
"gap_test": {
"gap_points": -3.8,
"threshold_points": 8,
"verdict": "noise"
}
},
"click_to_lead_severity": "normal",
"click_to_lead_vs_own_window": {
"rate_pct": 7.1,
"gap_test": {
"gap_points": -0.5,
"threshold_points": 8.9,
"verdict": "noise"
},
"reading": "worse (noise)"
}
}
],
"devices": [
{
"device": "mobile",
"impressions": 49440,
"clicks": 3112,
"leads": 332,
"ctr_pct": 6.3,
"click_to_lead_pct": 10.7,
"spend_usd": 17740,
"vs_other_devices": {
"gap_points": 0,
"threshold_points": 2.5,
"verdict": "noise"
},
"reading": "no real gap",
"click_to_lead_severity": "normal"
},
{
"device": "desktop",
"impressions": 10506,
"clicks": 622,
"leads": 74,
"ctr_pct": 5.9,
"click_to_lead_pct": 11.9,
"spend_usd": 3540,
"vs_other_devices": {
"gap_points": 1.5,
"threshold_points": 2.7,
"verdict": "noise"
},
"reading": "no real gap",
"click_to_lead_severity": "normal"
},
{
"device": "tablet",
"impressions": 1854,
"clicks": 156,
"leads": 9,
"ctr_pct": 8.4,
"click_to_lead_pct": 5.8,
"spend_usd": 890,
"vs_other_devices": {
"gap_points": -5.1,
"threshold_points": 5,
"verdict": "real"
},
"reading": "real gap",
"click_to_lead_severity": "high"
}
],
"regions": [
{
"region": "The home region (the service area)",
"impressions": 60300,
"clicks": 3830,
"leads": 411,
"spend_usd": 21850,
"click_to_lead_pct": 10.7,
"vs_the_rest": {
"gap_points": 4.1,
"threshold_points": 8,
"verdict": "noise"
},
"reading": "no real gap",
"click_to_lead_severity": "normal"
},
{
"region": "Other regions (4, added together)",
"impressions": 1500,
"clicks": 60,
"leads": 4,
"spend_usd": 330,
"click_to_lead_pct": 6.7,
"vs_the_rest": {
"gap_points": -4.1,
"threshold_points": 8,
"verdict": "noise"
},
"reading": "no real gap",
"click_to_lead_severity": "normal"
}
],
"landing_pages": [
{
"landing_page": "AC-repair page",
"clicks": 1050,
"leads": 98,
"click_to_lead_pct": 9.3,
"spend_usd": 6300,
"vs_other_pages": {
"gap_points": 1.4,
"threshold_points": 2,
"verdict": "noise"
},
"severity": "normal"
},
{
"landing_page": "Home page",
"clicks": 820,
"leads": 75,
"click_to_lead_pct": 9.1,
"spend_usd": 1450,
"vs_other_pages": {
"gap_points": 1,
"threshold_points": 2.2,
"verdict": "noise"
},
"severity": "normal"
},
{
"landing_page": "Emergency-plumber page",
"clicks": 618,
"leads": 62,
"click_to_lead_pct": 10,
"spend_usd": 4150,
"vs_other_pages": {
"gap_points": 2,
"threshold_points": 2.4,
"verdict": "noise"
},
"severity": "normal"
},
{
"landing_page": "Water-heater page",
"clicks": 470,
"leads": 41,
"click_to_lead_pct": 8.7,
"spend_usd": 3100,
"vs_other_pages": {
"gap_points": 0.4,
"threshold_points": 2.7,
"verdict": "noise"
},
"severity": "normal"
},
{
"landing_page": "Drain-cleaning page",
"clicks": 400,
"leads": 9,
"click_to_lead_pct": 2.2,
"spend_usd": 2600,
"vs_other_pages": {
"gap_points": -6.8,
"threshold_points": 2.9,
"verdict": "real"
},
"severity": "critical"
},
{
"landing_page": "Furnace-repair page",
"clicks": 330,
"leads": 26,
"click_to_lead_pct": 7.9,
"spend_usd": 2200,
"vs_other_pages": {
"gap_points": -0.5,
"threshold_points": 3.2,
"verdict": "noise"
},
"severity": "normal"
},
{
"landing_page": "HVAC-installation page",
"clicks": 120,
"leads": 7,
"click_to_lead_pct": 5.8,
"spend_usd": 1900,
"vs_other_pages": {
"gap_points": -2.6,
"threshold_points": 5.1,
"verdict": "noise"
},
"severity": "normal"
},
{
"landing_page": "Service-area page",
"clicks": 60,
"leads": 3,
"click_to_lead_pct": 5,
"spend_usd": 330,
"vs_other_pages": {
"gap_points": -3.4,
"threshold_points": 7.2,
"verdict": "noise"
},
"severity": "normal"
},
{
"landing_page": "Free estimate page",
"clicks": 22,
"leads": 3,
"click_to_lead_pct": 13.6,
"spend_usd": 150,
"vs_other_pages": {
"verdict": "too few to compare"
},
"severity": "not graded (under 30 clicks)"
}
]
},
"segment_notes": [
"Each rate has one reference that sets its severity: a campaign's impression share and click rate against its own comparison window; click-to-lead against the other non-brand campaigns (Brand against its own window), devices against the other devices and landing pages against the other pages. The other comparison is shown as context only.",
"The 'low below' lines (30% impression share, 2% click rate, 4% click to lead) are floors for the account totals and every non-brand segment, with or without history; Brand is exempt.",
"Campaigns or pages with under 30 clicks are listed but not graded.",
"4 other regions each sent under 30 clicks, so they are added together and never graded one by one. Every click from them came from the Out of Area campaign, which targets the nearby regions on purpose, and every other campaign's clicks came from the home region, so here the region split repeats the campaign split.",
"The free estimate page turned 3 of 22 ad clicks into leads: too few to grade, but a candidate page to test against the weak drain-cleaning page.",
"Leads from the landing-page report (324) are fewer than the account's 415, because calls from ads have no landing page."
],
"leaks": [
{
"step": "Eligible impressions โ impressions",
"segment": "AC Repair campaign",
"severity": "high",
"current": "52.0% impression share",
"reference": "71.0% in the comparison window",
"counted_part": "budget",
"counted_part_note": "Lost 31.0% to budget and 17.0% to rank. Both parts count (leads cost $50, at or under the account's $53); the budget part costs more leads, so it sets the impact.",
"missed_impressions_to_budget": 12519,
"missed_impressions_to_rank": 6865,
"leads_a_month_ceiling": 75,
"extra_spend_a_month_ceiling_usd": 3760,
"cost_per_lead_usd": 50,
"impact": "high"
},
{
"step": "Eligible impressions โ impressions",
"segment": "Emergency Plumbing campaign",
"severity": "moderate",
"current": "55.0% impression share",
"reference": "68.0% in the comparison window",
"counted_part": "budget",
"counted_part_note": "Lost 27.0% to budget and 18.0% to rank. Both parts count (leads cost $52, at or under the account's $53); the budget part costs more leads, so it sets the impact.",
"missed_impressions_to_budget": 4811,
"missed_impressions_to_rank": 3207,
"leads_a_month_ceiling": 41,
"extra_spend_a_month_ceiling_usd": 2110,
"cost_per_lead_usd": 52,
"impact": "high"
},
{
"step": "Clicks โ leads",
"segment": "Drain Cleaning campaign",
"severity": "critical",
"current": "3.0% of 400 clicks",
"reference": "11.5% on the other non-brand campaigns",
"leads_a_month_lost": 34,
"spend_usd": 2600,
"cost_per_lead_usd": 217,
"excess_spend_a_month_usd": 1960,
"impact": "high"
},
{
"step": "Eligible impressions โ impressions",
"segment": "Brand campaign",
"severity": "moderate",
"current": "86.0% impression share",
"reference": "93.0% in the comparison window",
"counted_part": "rank",
"counted_part_note": "Lost 14.0% to rank and none to budget, so the rank part sets the impact.",
"missed_impressions_to_budget": 0,
"missed_impressions_to_rank": 684,
"leads_a_month_ceiling": 16,
"extra_spend_a_month_ceiling_usd": 240,
"cost_per_lead_usd": 15,
"impact": "low"
},
{
"step": "Eligible impressions โ impressions",
"segment": "HVAC Installation campaign",
"severity": "moderate",
"current": "38.0% impression share",
"reference": "45.0% in the comparison window",
"counted_part": "rank",
"counted_part_note": "Lost 62.0% to rank and none to budget, so the rank part sets the impact.",
"missed_impressions_to_budget": 0,
"missed_impressions_to_rank": 5058,
"leads_a_month_ceiling": 15,
"extra_spend_a_month_ceiling_usd": 3100,
"cost_per_lead_usd": 211,
"impact": "low"
}
],
"priority_matrix": {
"do first": [
"#1 Leads: Confirm what 'Tap to call (website)' records",
"#2 Below leads: Import the step after the lead: booked jobs from the job management system as an offline conversion, with a job value, so the funnel reaches revenue and leaks can be priced",
"#3 AC Repair campaign: Move budget from the Drain Cleaning campaign (the most expensive leads) into this campaign, then raise it in steps while its cost per lead stays under the account's",
"#4 Emergency Plumbing campaign: Move budget from the Drain Cleaning campaign (the most expensive leads) into this campaign, then raise it in steps while its cost per lead stays under the account's",
"#5 Drain Cleaning campaign: Rebuild the drain-cleaning page above the fold for mobile: the problem named in the ad as the headline, a click-to-call button and a 3-field booking form, plus reviews and the price range"
],
"plan next": [],
"quick wins": [
"#6 Brand campaign: Raise the brand keywords' bids or target impression share so the company's own name shows on every search, and check the brand ads' quality"
],
"later": [
"#7 HVAC Installation campaign: Leave its budget as it is"
]
},
"top_fixes": [
{
"rank": 1,
"type": "trust",
"step": "Leads",
"fix": "Confirm what 'Tap to call (website)' records. If it is a button tap, move it to a secondary action and count a confirmed call (call tracking with a minimum length) instead.",
"why": "'Tap to call (website)' is a counted website action and recorded 142 of the 415 leads (34.2%). Its name and type suggest a tap on a call button, not a completed call. If so, the lead step is inflated by up to that share.",
"effort": "low",
"go_deeper": "measurement/conversion-tracking-auditor (if installed)",
"quadrant": "do first"
},
{
"rank": 2,
"type": "trust",
"step": "Below leads",
"fix": "Import the step after the lead: booked jobs from the job management system as an offline conversion, with a job value, so the funnel reaches revenue and leaks can be priced.",
"why": "Conversion value is 0 on every campaign, and no qualified-lead or booked-job step is imported. The funnel ends at leads, and impact is counted in leads and spend.",
"effort": "medium",
"go_deeper": "measurement/conversion-tracking-auditor (if installed)",
"quadrant": "do first"
},
{
"rank": 3,
"type": "leak",
"step": "Eligible impressions โ impressions",
"segment": "AC Repair campaign",
"severity": "high",
"current": "52.0% impression share",
"reference": "71.0% in the comparison window",
"impact": "up to 75 more leads a month (a ceiling) for up to $3,760 more spend, at a cost per lead of $50 against the account's $53",
"cause_hypothesis": "The daily budget runs out: 12,519 eligible impressions were lost to budget in the window.",
"evidence": "Call 13: lost-to-budget share up on the comparison window.",
"fix": "Move budget from the Drain Cleaning campaign (the most expensive leads) into this campaign, then raise it in steps while its cost per lead stays under the account's.",
"effort": "low",
"go_deeper": "google-ads/budget-allocation-optimizer",
"impact_level": "high",
"quadrant": "do first"
},
{
"rank": 4,
"type": "leak",
"step": "Eligible impressions โ impressions",
"segment": "Emergency Plumbing campaign",
"severity": "moderate",
"current": "55.0% impression share",
"reference": "68.0% in the comparison window",
"impact": "up to 41 more leads a month (a ceiling) for up to $2,110 more spend, at a cost per lead of $52 against the account's $53",
"cause_hypothesis": "The daily budget runs out: 4,811 eligible impressions were lost to budget in the window.",
"evidence": "Call 13: lost-to-budget share up on the comparison window.",
"fix": "Move budget from the Drain Cleaning campaign (the most expensive leads) into this campaign, then raise it in steps while its cost per lead stays under the account's.",
"effort": "low",
"go_deeper": "google-ads/budget-allocation-optimizer",
"impact_level": "high",
"quadrant": "do first"
},
{
"rank": 5,
"type": "leak",
"step": "Clicks โ leads",
"segment": "Drain Cleaning campaign",
"severity": "critical",
"current": "3.0% of 400 clicks",
"reference": "11.5% on the other non-brand campaigns",
"impact": "about 34.0 leads a month lost; $2,600 spent at $217 a lead against the account's $53, so about $1,960 a month buys nothing extra",
"cause_hypothesis": "The step after the click, not the ad: the campaign's click rate rose from 4.2% to 5.6%, so the ad draws clicks, but the leads don't follow. Its landing page turns 2.2% of 400 clicks into leads against 9.0% on the other landing pages; that page gap is real on its own. Likely causes: the page doesn't answer the search (the problem, the price, how soon a plumber arrives), the call button and booking form sit low on mobile, or there is no proof such as reviews and a guarantee.",
"evidence": "Calls 14 and 17; confirm with a session recording or a form-field report, and check the page's mobile speed.",
"fix": "Rebuild the drain-cleaning page above the fold for mobile: the problem named in the ad as the headline, a click-to-call button and a 3-field booking form, plus reviews and the price range. Until it converts, cap the campaign's budget.",
"effort": "medium",
"go_deeper": "marketing/ad-landing-page-match-scorer, marketing/landing-page-auditor",
"impact_level": "high",
"quadrant": "do first"
}
],
"more_fixes": [
{
"rank": 6,
"type": "leak",
"step": "Eligible impressions โ impressions",
"segment": "Brand campaign",
"severity": "moderate",
"current": "86.0% impression share",
"reference": "93.0% in the comparison window",
"impact": "up to 16 more leads a month (a ceiling)",
"cause_hypothesis": "Ad rank: 14.0% of brand impressions were lost to rank and none to budget.",
"evidence": "Call 13: lost-to-rank and lost-to-budget shares.",
"fix": "Raise the brand keywords' bids or target impression share so the company's own name shows on every search, and check the brand ads' quality; a competitor bidding on the name is the usual cause of brand share lost to rank.",
"effort": "low",
"go_deeper": "google-ads/impression-share-analyst (if installed)",
"impact_level": "low",
"quadrant": "quick win"
},
{
"rank": 7,
"type": "leak",
"step": "Eligible impressions โ impressions",
"segment": "HVAC Installation campaign",
"severity": "moderate",
"current": "38.0% impression share",
"reference": "45.0% in the comparison window",
"impact": "up to 15 more leads a month (a ceiling)",
"cause_hypothesis": "Lost 62.0% to rank and none to budget, so the rank part sets the impact.",
"evidence": "Call 13: lost-to-rank and lost-to-budget shares.",
"fix": "Leave its budget as it is; before adding any, find out why its leads cost so much (its keywords, its ads and its landing page), then raise bids only if the cost per lead comes down.",
"effort": "medium",
"go_deeper": "google-ads/keyword-performance-analyzer",
"impact_level": "low",
"quadrant": "later"
}
],
"tests": [
{
"leak": "AC Repair campaign impressions lost to budget",
"hypothesis": "If the budget rises in steps for 2 weeks each, leads rise while the cost per lead stays under the account's $53, because 12,519 eligible impressions went unserved.",
"type": "stepped budget test (not a split test)",
"stop_rule": "Stop raising when 2 weeks' cost per lead goes above $53."
},
{
"leak": "Emergency Plumbing campaign impressions lost to budget",
"hypothesis": "If the budget rises in steps for 2 weeks each, leads rise while the cost per lead stays under the account's $53, because 4,811 eligible impressions went unserved.",
"type": "stepped budget test (not a split test)",
"stop_rule": "Stop raising when 2 weeks' cost per lead goes above $53."
},
{
"leak": "Drain Cleaning clicks โ leads",
"hypothesis": "If the drain-cleaning page leads with the problem named in the ad, a click-to-call button and a 3-field booking form above the fold on mobile, its click-to-lead rate rises from 3.0% towards 11.5%, because visitors see their problem and the next step without scrolling.",
"clicks_per_version": 143,
"months_at_current_volume": 0.7,
"note": "At this volume a split test takes months; run the new page for all traffic for 30 days and compare with this window instead, using the same gap test."
}
],
"monitoring": [
{
"metric": "Impression share (all Search)",
"current_pct": 56.2,
"warning_below_pct": 66,
"critical_below_pct": 33.1,
"check": "weekly"
},
{
"metric": "Impressions โ clicks",
"current_pct": 6.3,
"warning_below_pct": 5.8,
"critical_below_pct": 3,
"check": "weekly"
},
{
"metric": "Clicks โ leads",
"current_pct": 10.7,
"warning_below_pct": 10.2,
"critical_below_pct": 5.5,
"check": "weekly"
},
{
"metric": "Mobile vs desktop gap, clicks โ leads",
"current_gap_points": -1.2,
"warning_when_gap_beyond_points": 2.7,
"note": "The gap that would count as real at this window's clicks; past it, check the mobile page first.",
"check": "weekly"
}
],
"not_checked": [
"Sessions that landed, engagement, form starts and the lead form report: GA4 is not connected for this business.",
"Whether ad clicks reach the site (the click-to-session check): needs GA4.",
"Booked jobs and revenue: not tracked. An offline conversion import of booked jobs from the job management system, with a job value, would add them (fix #2).",
"Mobile speed of the drain-cleaning page: run Core Web Vitals Fixer (if installed) before the rebuild.",
"Meta, LinkedIn and Search Console: the business doesn't advertise there through this workspace, so those rows are left out."
],
"write_actions_offered": [],
"write_actions_note": "None offered: the GA4 key event and drop-off audience need GA4, which isn't connected for this business. Budget moves are handed to Budget Allocation Optimizer."
}
ReadyFunnel Drop-off Diagnostician
MarkdownUTF-8