Lead Quality Analysis

Josh Halpern Law

Why the ads were pulling the wrong leads — and what we did about it
April 2026 · Last 14-30 days · act_4058704351047507
Cynthia.

The ads are working. The wrong people are seeing them.

Josh's ad funnel is converting at a healthy $50.45 cost-per-lead — that part is fine. The problem is who's filling out the form. Roughly 50% of the leads who write anything in the "additional info" field describe a litigation, family law, criminal, personal injury, or court matter. Then they book a 15-minute consult with Josh, who shows up to a call about a federal court appeal or a social-security disability case — work he doesn't do.

We diagnosed three root causes, fixed the two that were code-deep, and just upgraded the Facebook audience targeting from a 15-customer Lookalike seed to a 582-customer, $1.08M-LTV value-weighted seed. The new ad set is built and paused, ready to flip on for a side-by-side test against the current Broad audience.

$756
Total Spend
15
Leads Generated
$50.45
Cost Per Lead
8,558
Impressions
292
Link Clicks
~50%
Wrong-Fit Rate
The real CPL on a qualified buyer is closer to $100, not $50. Headline CPL looks great because Meta counts every form fill as a lead — including the litigation leads. Halve the leads, double the cost.

Three causes, in order of impact

CAUSE 01
The deployed intake form has no real disqualifier
A stricter version of the intake modal — one that blocks submission for litigation/family/criminal/PI cases — exists in the source code but was never built and deployed. The version live on /lawyeroncall right now has 13 generic options plus an "Other" choice that shows a soft amber message saying "you're welcome to still submit and we'll let you know." Nothing actually stops a wrong-fit lead from booking.
Highest impact — this single fix kills ~30-50% of bad leads at the source.
CAUSE 02
The Lookalike seed had only 15 customers
The "Lookalike 1% — Stripe Subscribers" audience was built from a 15-person seed. That's not enough signal for Meta to learn anything meaningful — at that size, Meta is essentially picking random Americans aged 28-55. Josh has 582 actual paying customers in Stripe. They just hadn't been uploaded.
Highest impact for ad-side training — fixed today.
CAUSE 03
"Broad - Advantage+ Audience" expands to consumers
The single active ad set targets "Small business" + "Entrepreneurship" interests with Meta's Advantage+ Audience expansion turned on. That expansion sweeps in adjacent consumer segments — landlord/tenant, real-estate-curious, side-hustle dabblers — many of whom have personal legal issues, not registered-business needs.
Medium impact — fixed in the new ad set with audience expansion explicitly disabled.

What the bad-fit leads actually wrote

Looking at the optional "additional info" field on the intake form for the last ~14 days. Each lead picked a business-sounding dropdown option, then described something Josh doesn't handle:

"Looking to draft an appeal letter to overturn dismissed complaint in Federal Court." Picked: "I need employment or HR legal guidance"
"I need legal help in both social security disability law and family law and advise to get the proper amount for the sale of my house which is being under sold by 230k." Picked: "I need ongoing general counsel"
"I have court on the 30." Picked: "Ongoing general counsel"
"Contract breaches." Picked: "Ongoing general counsel" (litigation, not contract drafting)
"Needs help to create family trust." Picked: "I need to form an LLC or corporation" (close to scope but mismatched)
Inbound email referencing case number for legal malpractice — full lawsuit caption, asking Josh to take it on. From a previous booked lead, post-call follow-up
Pattern: leads pick the closest-sounding dropdown option, then describe their actual need (which is litigation) in the free-text field. By that point they're already through the gate. The dropdown isn't a real qualifier — it's a soft suggestion.

Audience upgrade: 15 → 582 seed customers

Pulled every Stripe customer (active + churned, all-time), tied each to their lifetime spend, hashed PII (SHA-256), and uploaded to Meta as a value-weighted Custom Audience. Then built a 1% Lookalike off that audience. The previous Lookalike was using only the 15 currently-active subscribers as its seed.

582
Customers Uploaded
466
With Paid Spend
$1.08M
Total LTV in Seed
$2,313
Avg LTV per Customer
$51,150
Top Customer LTV
39×
More Seed Signal

What was created in the ad account today

Asset Type Status
Stripe Customers — All-Time (Value-Weighted) Custom Audience Updating (30-60 min)
LAL 1% US — Stripe Customers (All-Time) Lookalike Audience Ready
LAL 1% US — Stripe Customers (All-Time, 582 seed) Ad Set (3 ads) Paused — awaiting go-live
The new ad set is fully built — same creative, same targeting age range, just a much smarter audience. Three top-performing ads (01 - Not Your Only Option, 07 - 500 Per Call Broken, 08 - Contract Thousands CTA) were cloned in. Advantage+ Audience expansion is explicitly disabled so Meta respects the Lookalike instead of "expanding" it back to consumers.

What we're doing — and what's still on the list

DONE
Built a real Lookalike audience from Josh's actual customer base
582 Stripe customers, value-weighted by lifetime spend. New 1% Lookalike sits at roughly the same precision tier as the old one but with 39× more seed signal. Meta has a real pattern to match against now.
DONE
Cloned the active ad set with the new audience
"LAL 1% US — Stripe Customers (All-Time, 582 seed)" sits inside the existing campaign, paused. Three ads cloned. Audience expansion disabled. Ready to go live with one click.
NEXT
Flip the new ad set ON
Once live, Meta's Campaign Budget Optimization will split the $100/day spend between the new LAL set and the existing Broad set. After 7-10 days we'll have side-by-side data on which audience converts cheaper and cleaner.
Expected: cleaner intake within 1-2 weeks as Meta learns from the new seed.
NEXT
Deploy the strict intake-form disqualifier
The fixed ContactModal already exists in the codebase — it just needs to be built and pushed. Once live, anyone selecting "I have a civil lawsuit, litigation, family, criminal, or personal injury matter" sees a red box recommending their state bar's referral service, and the submit button greys out.
Highest single-fix impact. Kills 30-50% of bad-fit leads before they ever reach Josh's calendar.
SOON
Make "additional info" required + filter for litigation keywords
Right now the field is optional with a soft placeholder. Make it required ("In one sentence, what's the legal need?"), and add a client-side keyword filter that catches words like lawsuit, court, divorce, custody, criminal, injury, evict, malpractice — same red-box treatment as the dropdown disqualifier.
SOON
Add a Calendly screening question
Two required questions on the booking page: "In one sentence, what do you need help with?" and "Is this for a registered business you own?" (yes/no). The second alone deflects most personal-legal-issue traffic before they grab a slot on Josh's calendar.
LATER
Build a "Bad Fit" exclusion audience that auto-syncs
Every lead tagged disqualified/litigation/family/PI gets pushed to a Meta Custom Audience set as a campaign-level exclusion. Meta starts learning the patterns those people share and avoids them. Over 30 days this is the closest thing to "negative training" Meta still allows since they killed interest-based exclusions.
LATER
Switch optimization from "Lead" to "Purchase"
Today Meta optimizes for any form fill. Once we wire Stripe → Meta CAPI Purchase events, Meta can optimize for actual paying customers — the gold standard. Requires building a Stripe webhook (code is drafted) and configuring it in Stripe Dashboard. Not blocking; the audience upgrade alone moves the needle first.

The 7-day scoreboard

Once the new ad set is live, these are the numbers that matter. We'll have a clean read by day 7-10.

Metric Baseline (Broad) Target (New LAL)
Cost per Lead $50.45 < $55 (acceptable if quality improves)
Wrong-fit rate (lawsuit/family/PI) ~50% of described leads < 20%
Lead → Booked rate ~12% (8 booked / ~66 form submits) > 20%
Booked → Showed-up rate ~80% (2 no-shows / 10 booked) > 85%
Showed-up → Qualified for retainer Unknown — Josh tracks this Track this in CRM going forward
The right north-star metric is "cost per qualified buyer," not "cost per lead." If the new LAL ad set delivers a $60 CPL with a 30% wrong-fit rate, that's a $86 qualified-buyer cost. Beats a $50 CPL with a 50% wrong-fit rate ($100). Quality > volume.

Three pulls of the lever — done, queued, and ready

1
DONE — Audience is upgraded
Meta now has 39× more signal about who Josh's buyers actually look like. New Lookalike + Custom Audience are live in the ad account. Cloned ad set is built and waiting.
2
DO NOW — Flip the new ad set ON
One toggle in Ads Manager. Same campaign, same daily budget. Side-by-side test against the existing Broad set begins immediately.
3
DO THIS WEEK — Deploy the form disqualifier
The single biggest lever for lead quality. Code is already written. Just needs to be built + pushed to Vercel.