The sole leak is the https://finder.cancerdoctor.com href, which exposes the business name even though the display text was already scrubbed. I'll also lowercase "Finder" (it reads as a brand name) — minimal touch, everything else stays.
The Setup
TLDR: If a URL already carries complete contact info and a specific destination, that's a booking — create the record at the route level, not behind a UI action the user has no reason to take.
We built a cancer education business finder to connect patients with integrative oncology centers.
High-intent traffic arrives via deep links from email campaigns and partner referrals — URLs like ?center=<takeshapeId>&firstName=...&lastName=...&email=...&phone=... where TakeShape is the headless CMS storing facility records.
The user's contact info is already in the URL. They were pre-qualified upstream. They clicked a specific center. That is intent.
What Broke
I noticed it first: the admin backend was showing these arrivals as Leads-only. No Consultations.
Campaign ROI, partner reporting, admin triage — all of it dark on the most valuable segment.
I dug into the root cause and found it immediately.
createConsultation was only ever called from one place: the Book button tap on a FacilityCard, which fired /api/consultations/bulk. The chat route at src/app/api/chat/route.ts was doing the right thing for Leads — calling findOrCreateLead and saving the row — but it never created a Consultation.
No Book tap = no Consultation row.
And here's the thing: pre-qualified deep-link users have no reason to tap Book. The URL already told us everything. We just weren't listening.
What I Tried First (and Why It Wasn't Enough)
The first fix was obvious: detect ?center= with complete params on the first turn of a new conversation and call createConsultation right there in the chat route, before the assistant response goes out.
That shipped. (fix(chat): auto-create consultation on deep-link arrival, April 20.)
But it only caught the arrival-with-complete-params path.
What about returning visitors whose lead pre-fills the form — and they click confirm on turn one? Or users who arrive with the center param but without all four contact fields, then fill the form in-session?
Both paths exist in the real funnel. Both were still dropping Consultations.
So the next day I wired a second gate: auto-create when the lead form submits inside a deep-link session, not just on arrival. (fix(chat): create consultation when lead form submits after deep-link, April 21.)
Now the full path was covered.
The Fix That Held
Both fixes converge on the same insight: the chat route is the natural place to make this decision.
That's where lead creation already happens. That's where the conversation state is known. Add a shouldAutoConsult gate — !isExistingConversation && hasCompleteDeepLinkParams — and call createConsultation in the same breath as findOrCreateLead.
We also decided, deliberately, to skip idempotency on the auto-create. If a user clears localStorage and revisits the same deep link, they might get a duplicate Consultation. I accepted that tradeoff — the !isExistingConversation check already prevents in-session duplication, and shipping fast mattered more than a watertight guard.
Why This Matters
The UI action (Book tap) was never the right signal for this segment.
The URL was.
When a deep link carries a destination AND complete contact info, you already have everything a Consultation record needs. Creating it should be automatic — at the route level, triggered by state inference, not gated behind a button the user doesn't need to press.
The general rule: don't build record creation around user actions when the data is already present. Infer the funnel state from what you know. Create the record. Match the intent that was signaled before the user arrived.
P.S. The lead-gate is still live — the AI stays warm but doesn't hand over contact info until the lead row is saved. Deep-link auto-consult fires after that confirmation, not before. Intent recognized doesn't mean contact info bypassed.