Lead Scoring Integration
Bridges the visitor tracking system and lead capture system so every lead is enriched with an engagement score, helping sales teams prioritize follow-ups.
How It Works
Visitor browses site → tracker.js records behavior → engagement data in D1
Visitor submits form → form.js reads visitor_id from localStorage
Lead API receives form → looks up session by visitor_id (or IP fallback)
Lead stored in D1 + sent to webhook → enriched with scoring fieldsData Flow
- tracker.js stores
visitor_idin localStorage as_pg_vid - form.js reads
_pg_vidand injects it as hidden field__visitor_id - lead.ts receives the form POST with
__visitor_id lookupVisitorScore()queries:- Primary:
sessions WHERE visitor_id = ?(most recent) - Fallback:
sessions WHERE ip = ?(if visitor_id not found)
- Primary:
- Fetches fingerprint for return visit count and VPN flag
- Calculates score inline and enriches the lead payload
- Webhook to Make.com carries full scoring data
- D1 Leads table stores scoring columns
Scoring Model (Real Estate Optimized)
| Factor | Points | Logic |
|---|---|---|
| Duration | 0-35 | 1 pt per 10 seconds, cap at ~6 min |
| Scroll Depth | 0-30 | 1 pt per 3.33% depth, full scroll = 30 |
| Clicks | 0-20 | 1 pt per click, cap at 20 |
| Return Visits | 0-15 | 5 pts per return session, cap at 3 returns |
| Total | 0-100 |
Why These Factors for Real Estate
- Duration: A buyer spending 5-10 min reading floor plans and amenities shows strong intent
- Scroll Depth: Reaching the pricing/contact section at the bottom = high intent
- Clicks: Gallery photos, floor plan tabs, brochure downloads = active interest
- Return Visits: Coming back to the same property page = serious buyer
Engagement Labels
| Score | Label | Recommended Action |
|---|---|---|
| 0-20 | Cold | Bounced or bot — low priority |
| 21-45 | Warm | Browsed briefly — follow up within 24h |
| 46-70 | Hot | Read the page, looked at details — call within 2h |
| 71-100 | Very Hot | Deep engagement + return visits — call immediately |
Webhook Payload (New Fields)
json
{
"visitor_id": "abc123...",
"session_id": "sess-xyz...",
"engagement_score": 72,
"engagement_label": "Very Hot",
"pages_visited": 3,
"session_duration_seconds": 340,
"return_visits": 2,
"vpn_suspected": 0
}D1 Schema Changes (Migration 005)
sql
ALTER TABLE Leads ADD COLUMN VisitorId TEXT;
ALTER TABLE Leads ADD COLUMN SessionId TEXT;
ALTER TABLE Leads ADD COLUMN EngagementScore REAL DEFAULT 0;
ALTER TABLE Leads ADD COLUMN EngagementLabel TEXT DEFAULT 'Cold';
ALTER TABLE Leads ADD COLUMN PagesVisited INTEGER DEFAULT 0;
ALTER TABLE Leads ADD COLUMN SessionDuration INTEGER DEFAULT 0;
ALTER TABLE Leads ADD COLUMN ReturnVisits INTEGER DEFAULT 0;
ALTER TABLE Leads ADD COLUMN VpnSuspected INTEGER DEFAULT 0;Graceful Degradation
- If tracker.js hasn't loaded → no
_pg_vidin localStorage →__visitor_idnot sent - If
__visitor_idhas no matching session → falls back to IP lookup - If no session found at all → lead saved with score 0, label "Cold"
- Scoring never blocks lead submission — errors caught and logged