Lead Management $179.99 / mo Updated 2026-04-25 VERSION 2 · NEW

📋 Lead Generation & Management — V2 Guide

Drop a form on your website, leads land in your CRM, agents work them, qualified leads become customers. Plus scoring, dedup, auto-reply emails, webhook events, CSV bulk import, and full agent workspace. V2 ships the dead schema activation that V1 promised but never wired.

Overview

RubiPro's flagship inbound + outbound lead module. The inbound side gives you an embeddable form widget — paste one snippet on your website (or your customer's website if you're an agency) and submissions appear in your CRM as leads. The outbound side gives you a CSV import wizard for bulk-loading leads from external sources, plus an agent workspace where agents work the queue, log dispositions, schedule callbacks, and convert qualified leads to customers.

V2 activates the dead schema pieces that V1 created but never engineered: lead scoring rules now compute quality_score (0-100) on every submission, duplicate detection flags incoming leads against existing tenant data, NotificationEngine fires for new-lead alerts, PHPMailer sends auto-replies + notify emails, WebhookManager dispatches lead.* events for external integrations.

The lead pipeline

1

Capture

Embeddable form widget on your site posts to /api/lead-capture.php. CSV import wizard for bulk loads. Mac app posts to /api/v2/leads.php with Bearer JWT.

2

Filter

5-layer spam protection: honeypot, submission timing, keyword blocklist, domain blocklist, per-IP rate limit. Repeat-offender IPs banned (with expiration).

3

Score

Configurable scoring rules compute quality_score 0-100 from field matches, source attribution, and behavioral signals. Test sandbox lets you verify rules before going live.

4

Dedupe

Email exact-match plus phone normalized-match (last 10 digits) against existing leads, list records, and customers. Flag-not-block — agents review and decide.

5

Notify

In-app notifications to configured users, email to notify_email recipient, auto-reply email to the lead with token replacement, webhook events to all subscribers.

6

Work

Agent workspace: My Queue / Callbacks / Get Lead / Search. Disposition codes with required-fields enforcement. Notes, callbacks, and history per lead.

7

Convert

One-click conversion creates a customer record, links lead to customer, copies notes and custom fields, preserves source attribution. lead.converted webhook fires.

What's new in V2

Lead scoring engine

LeadEngine::applyScoringRules walks active lead_scoring_rules at submission time, sums points (with optional per-rule cap), clamps 0-100, and writes the result to web_leads.quality_score. The tenant-admin Scoring Rules manager has a built-in test sandbox: paste sample lead JSON, see the score before saving the rule.

Duplicate detection

LeadEngine::findDuplicates checks email + normalized phone against existing leads, list records, and customers within the tenant. Web submissions land with status='duplicate' and a duplicate_of pointer to the matched record; CSV imports honor the configured duplicate_action (skip / merge / keep_both) per row.

Notifications + auto-reply

LeadEngine::dispatchLeadEvent fires NotificationEngine for in-app alerts to notify_users, PHPMailer for the form's notify_email, plus an auto-reply to the lead with full token replacement ({{first_name}}, {{company}}, {{form_name}}, etc.).

Webhook events — dispatched

Six lifecycle events emit via WebhookManager: lead.created, lead.qualified, lead.converted, lead.rejected, lead.assigned, lead.disposition_recorded. Payload includes contact fields, status, quality_score, UTM tags, plus event-specific extras. Best-effort delivery — failures error_log() but never block submission.

Form preview

New /tenant-admin/lead-form-preview.php?uuid=… page renders the live widget exactly as customers will see it — no need to embed on a real site to verify the form. Submissions in preview mode are real leads (sandbox accordingly).

State viewer + tightened migration page

Sysadmin /admin/view-leadgen-state.php shows column-level schema check, per-tenant activity stats, forms catalog with live submission counts, recent leads with quality bars, recent activity timeline, top spam IPs. Migration page upgraded from a 5-line disabled stub to V2 verification + per-tenant overview.

Agent CSV export

New "Export queue to CSV" button in My Queue. Pulls the agent's current open leads (non-converted, non-DNC, non-bad-number) and downloads as CSV. Admin role can request scope=all for tenant-wide export.

Mac-app contract preserved

/api/v2/leads.php response shapes verbatim. Mac app keeps working without any code changes. The same dispatchLeadEvent fires from both /api/lead-capture.php and /api/v2/leads.php public submission paths so notifications + webhooks fire regardless of source.

Embedding the form

After building a form in Tenant Admin → Lead Forms, click Embed to copy the snippet:

<div id="rubi-lead-form" data-form-uuid="YOUR-FORM-UUID-HERE"></div>
<script src="https://rubiprofessional.com/js/lead-widget.js?v=3"></script>

Paste it into your website's HTML wherever you want the form. The widget loads asynchronously, doesn't block page render, and styles itself with sensible defaults. Multiple forms on the same page work — each div's data-form-uuid identifies which form to render.

Click Preview on a form to see exactly what customers will see. The preview page loads the same widget against a real production endpoint — submissions in preview ARE recorded as leads, so test with sandbox data.

Lead scoring

Tenant Admin → Lead Scoring Rules. Create rules with:

  • Type: field_match (most common — match a form field), behavior, source, engagement.
  • Field: any submitted form field (email, phone, company, utm_source, utm_campaign, utm_medium, utm_term, utm_content, etc.) plus extracted standards (first_name, last_name).
  • Operator: equals, contains, starts_with, ends_with, greater_than, less_than, in_list (comma-separated), not_in_list.
  • Match value: what to compare against (case-insensitive for text operators).
  • Points: positive or negative integer.
  • Max points (optional): cap on this rule's contribution.

Examples:

  • "Enterprise email" — field=email, operator=ends_with, value=".com", points=+10, max=10
  • "Free email penalty" — field=email, operator=in_list, value="gmail.com,yahoo.com,hotmail.com", points=-5
  • "Has phone" — field=phone, operator=starts_with, value="", points=+15 (any non-empty phone)
  • "Paid traffic" — field=utm_source, operator=in_list, value="google,facebook,linkedin", points=+20
  • "Has company" — field=company, operator=starts_with, value="", points=+15

The Test sandbox lets you paste sample lead JSON and see the resulting score before committing the rule live. Score is clamped 0-100 — negative totals floor at 0.

Duplicate detection

On every web submission, V2 checks the incoming email + phone against three sources within the current tenant:

  • web_leads — open leads (status NOT IN spam/rejected/duplicate)
  • lead_records — open list records (status NOT IN converted/dnc/bad_number)
  • customers — already-converted

Match conditions: email is case-insensitive exact-match. Phone is normalized to last-10-digits (strip non-digits, take last 10). If either matches, the new lead is inserted with status='duplicate' and duplicate_of pointing to the matched lead. Agents see the duplicate flag in the workspace and decide whether to merge, dismiss, or escalate.

For CSV imports, the import wizard's duplicate_action setting controls behavior per-row: skip (don't import), merge (update existing record), keep_both (insert anyway, no flag). All matching is tenant-scoped.

Notifications + webhooks

When a non-spam lead lands, V2 fires three notification channels and one webhook channel — all best-effort, none block submission:

  • In-app: NotificationEngine sends to every user_id in the form's notify_users JSON array. Notification has a deep link to the lead in the CRM.
  • Email to admin: the form's notify_email field gets a plain-text alert with full lead details + link to CRM.
  • Auto-reply to lead: if auto_reply_enabled and the lead provided an email, PHPMailer sends the configured subject/body with token replacement: {{first_name}}, {{last_name}}, {{email}}, {{phone}}, {{company}}, {{form_name}}.
  • Webhook: WebhookManager dispatches lead.created with payload {lead_id, form_id, form_name, first_name, last_name, email, phone, company, status, quality_score, utm_*, created_at, extras}. Subscribers configure their endpoints in Tenant Admin → Webhooks.

Other lifecycle events with webhook dispatch: lead.converted (lead → customer), lead.qualified, lead.rejected, lead.assigned, lead.disposition_recorded.

Agent workspace

Agents open Leads in the CRM. Four sub-tabs:

  • My Queue — leads currently assigned to me, sorted overdue-callbacks-first. CSV export available.
  • Callbacks — three groupings: overdue, today, upcoming.
  • Get Lead — pulls the next available lead from the tenant pool. Three-tier priority: overdue callbacks for me → leads assigned to me → unassigned cherry-pick or round-robin per the lead list's distribution_mode.
  • Search — full-text across leads + lead_records by name / email / phone / company.

Per-lead actions: log disposition (with required-notes / required-callback enforcement per the disposition_code config), schedule callback, add note, unlock (release back to the pool), assign to another agent, convert to customer.

Conversion preserves: notes (with "[From Lead]" prefix), custom fields (mapped per the lead list's column_mapping), source attribution, and writes a creation note on the customer with the lead source.

CSV bulk import

Tenant Admin → Lead Lists → Import. 4-step wizard:

  1. Upload + name. Pick a CSV, name the list, set list_type (outbound / callback / priority).
  2. Map columns. Preview the first 10 rows, drag CSV headers to standard fields (first_name, last_name, email, phone, address, city, state, zip, notes) plus any custom fields you've configured.
  3. Settings. Priority, max attempts before retire, distribution_mode (cherry_pick / round_robin / manual), default agent assignment, duplicate action (skip / merge / keep_both).
  4. Confirm. Run the import. Each row passes through duplicate detection, lands in lead_records (or skips per duplicate_action), and the list is ready for agents.

Imports run synchronously in the request handler — large files (10k+ rows) may take a few seconds. Future versions will queue large imports for background processing.

Spam protection

Five layers, scored 0-1.0. Spam_score ≥ 0.7 → status='spam' (still recorded for audit, never delivered to agent inbox):

  • Honeypot — invisible field bots fill in. +0.6 spam score if triggered.
  • Submission timing — minimum seconds from page load to submit (default 3, configurable). +0.4 if too fast.
  • Keyword blocklist — JSON array of flagged keywords. +0.3 if found in any field.
  • Domain blocklist — JSON array of blocked email domains. +0.5 if email matches.
  • IP rate limiting — max submissions per IP per 24 hours (default 10, configurable per form). Hard-block at the limit.

Repeat-offender IPs can be added to lead_spam_ips with optional expiration. Submissions from blocked IPs are silently accepted but never recorded — the bot doesn't get feedback that it's blocked.

API + integrations

Public submission

POST /api/lead-capture.php
Content-Type: application/json

{
  "form_uuid": "abc-123-...",
  "form_data": { "first_name": "...", "email": "...", "phone": "...", ... },
  "submission_time": 7,                            // seconds from page load
  "source_url": "https://example.com/contact",
  "utm_source": "google", "utm_medium": "cpc", ...
}

Mac-app v2 endpoints (preserved verbatim)

GET  /api/v2/leads.php             → list leads
GET  /api/v2/leads.php?id=123       → one lead
POST /api/v2/leads.php/submit       → public submission
POST /api/v2/leads.php/convert/123  → convert lead → customer
PUT  /api/v2/leads.php/leads/123    → update lead
GET  /api/v2/leads.php?segments=forms     → list forms

Webhook subscribers

Configure outbound webhooks in Tenant Admin → Webhooks. Subscribe to events: lead.created, lead.converted, lead.qualified, lead.rejected, lead.assigned, lead.disposition_recorded.

Frequently asked questions

How does the embeddable form widget work?
Each form has a unique UUID. The embed snippet is a div with data-form-uuid + a script tag pointing at /js/lead-widget.js. The widget fetches the form config via CORS, renders fields, applies validation, and POSTs submissions to /api/lead-capture.php. ~16KB minified, async, doesn't block page render.
What does the LeadEngine do?
LeadEngine is the single source of truth for the inbound lead pipeline: scoring rules computed on every submission, duplicate detection across leads + list records + customers, NotificationEngine + PHPMailer wired for in-app + email alerts, WebhookManager dispatching six lifecycle events, lead-to-customer conversion preserving notes / custom fields / source attribution. The 16-table schema and the public form widget, form builder, agent workspace, and CSV import wizard all integrate with the engine without changes to the surfaces.
How does lead scoring work?
Rules in lead_scoring_rules with type / field_name / operator / value / points / max_points. On submission, applyScoringRules walks active rules, sums matched points (capped at max_points per rule), clamps 0-100, writes to web_leads.quality_score. Tenant admin scoring rules manager has a test sandbox.
How does duplicate detection work?
Email exact-match (case-insensitive) and phone normalized-match (last 10 digits) against existing leads, list records, and customers in tenant scope. Web matches → status='duplicate' with duplicate_of pointer. CSV imports honor configured action (skip / merge / keep_both).
What lifecycle events fire webhooks?
Six events: lead.created / lead.qualified / lead.converted / lead.rejected / lead.assigned / lead.disposition_recorded. Payload: contact fields, status, quality_score, UTM tags, plus event-specific extras. Best-effort delivery via WebhookManager.
How is tenant isolation enforced?
Every engine method runs tenant verification before any write. Form lookup at submission validates form_uuid → tenant. Scoring rules + duplicate checks + leadbox queries all filter by tenant_id. Cross-tenant POSTs match zero rows and return generic not-found errors.
Auto-reply and notification emails — what fires?
Three emails per non-spam lead: auto-reply to the lead (if enabled, with token replacement), notify_email to the form admin, in-app notify to notify_users via NotificationEngine. All best-effort.
How does CSV import work?
4-step wizard: upload + name → map columns → settings (priority, max attempts, distribution_mode, duplicate_action) → import. Each row passes through duplicate detection per the chosen action.
How do agents work leads?
Lead Workspace with My Queue / Callbacks / Get Lead / Search tabs. Per-lead: log disposition (with enforcement), schedule callback, add note, assign, convert. Get Lead three-tier priority: overdue callbacks → assigned → unassigned cherry-pick.
Spam protection
5 layers: honeypot, submission timing, keyword blocklist, domain blocklist, per-IP rate limit. Spam score ≥0.7 → marked status=spam (recorded for audit, hidden from agent inbox). Repeat IPs go in lead_spam_ips with optional expiration.
Does this work with the macOS app?
Yes. /api/v2/leads.php contract preserved verbatim. V2 adds dispatchLeadEvent to both /api/lead-capture.php and /api/v2/leads.php public paths so notifications + webhooks fire regardless of source.
How much does this cost?
$179.99 / month / tenant. Unlimited forms, submissions, agents, webhook subscribers, and CSV imports. No per-lead or per-seat fees.

Ready to capture leads on autopilot?

Activate, build a form, embed on your site. Leads land in your CRM in under 15 minutes.