Skip to content

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 fields

Data Flow

  1. tracker.js stores visitor_id in localStorage as _pg_vid
  2. form.js reads _pg_vid and injects it as hidden field __visitor_id
  3. lead.ts receives the form POST with __visitor_id
  4. lookupVisitorScore() queries:
    • Primary: sessions WHERE visitor_id = ? (most recent)
    • Fallback: sessions WHERE ip = ? (if visitor_id not found)
  5. Fetches fingerprint for return visit count and VPN flag
  6. Calculates score inline and enriches the lead payload
  7. Webhook to Make.com carries full scoring data
  8. D1 Leads table stores scoring columns

Scoring Model (Real Estate Optimized)

FactorPointsLogic
Duration0-351 pt per 10 seconds, cap at ~6 min
Scroll Depth0-301 pt per 3.33% depth, full scroll = 30
Clicks0-201 pt per click, cap at 20
Return Visits0-155 pts per return session, cap at 3 returns
Total0-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

ScoreLabelRecommended Action
0-20ColdBounced or bot — low priority
21-45WarmBrowsed briefly — follow up within 24h
46-70HotRead the page, looked at details — call within 2h
71-100Very HotDeep 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_vid in localStorage → __visitor_id not sent
  • If __visitor_id has 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