Lead Capture API
Endpoint
| Method | Path | Description |
|---|---|---|
POST | /lead.capture | Primary — accepts lead form submissions |
POST | /lead | Deprecated — backward compatible, emits X-Deprecated + Sunset headers |
POST | / | Deprecated — backward compatible, emits X-Deprecated + Sunset headers |
Migration notice: The catch-all lead handler has been replaced by the explicit
/lead.captureroute. The legacy/leadand/paths remain functional but will be removed after 2026-06-01. Update your integrations to usePOST /lead.capture.
Authentication (required)
Every submission must be authenticated in one of two ways. Requests that have neither are rejected with 403 Forbidden.
MODE 1 — Server integration (lead capture API key)
If the request includes an X-API-Key header (lead capture API key, created in Forms config → Integrations):
- The key is validated against the
Integrationstable. - If invalid → 401 Unauthorized.
- If valid, domain validation is skipped and the lead is stored. Optional
siteKeyandformIdin the body are stored for attribution.
Use this for server-to-server integrations (e.g. server-side form handlers, CRMs, webhooks). This key is not the same as the CRM callback secret (used for /lead.capture/crm-callback and /lead.capture/make-callback; configured in Settings or CRM_CALLBACK_SECRET).
MODE 2 — Browser embedded form (siteKey + formId)
If X-API-Key is not present:
- The body must include both
siteKeyandformId. If either is missing or empty → 403 Forbidden with a clear error message. - The request domain (from
OriginorReferer) is validated againstSiteDomainsfor that site. If not allowed → 403 Forbidden. - Site and form must be enabled in the dashboard; otherwise → 403 Forbidden.
siteKeyandformIdare stored for attribution.
Blocked IP
If the request IP is in the BlockedIPs table (see IP Blocking), the submission is rejected before any lead logic runs: it is not stored in Leads or LeadSubmissions and is not sent to Make.com. The attempt is recorded in BlockedIPLeads for audit. The client still receives the same success response (e.g. 301 redirect) so the form does not reveal that the IP is blocked.
How It Works
- Blocked IP check — If the request IP is in BlockedIPs, reject (record in BlockedIPLeads, return success/redirect; no further steps).
- Accepts
POSTrequests with form data (JSON,application/x-www-form-urlencoded,multipart/form-data, ortext/plain) - Extracts lead fields: name, email, phone, project, location, team
- Enriches with geolocation data from Cloudflare headers
- Looks up visitor's engagement score from tracking sessions
- Calculates suspicion score (honeypot, time-to-submit, form velocity)
- Finds related projects the visitor previously inquired about
- Checks for bot leads (when
BOT_LEAD_DETECTIONis enabled) - Resolves canonical lead by identity + project (
visitor_id> email > phone > IP) - Writes every submit into
LeadSubmissionshistory table - Forwards the enriched payload to a Make.com webhook for new primary leads
- Stores canonical lead in D1
Leadstable - Returns a
301redirect to a thank-you page
Expected Fields
| Field | Description |
|---|---|
FIRSTNAME | Lead's first name |
EMAIL | Lead's email address |
PHONE | Lead's phone number |
PROJECT | Real estate project name |
LOCATION | City/location |
TEAM | Source team identifier |
DOMAIN | Source for 301 redirect: full URL (e.g. https://example.com/page), hostname-only (e.g. example.com), or path-only (e.g. /upcoming-pune/v/). Path-only is resolved using the request Referer origin, e.g. https://www.example.com/upcoming-pune/v/. |
SOURCE | Lead source (WebSite, FB, 99) |
COUNTRYCODE | Country dial code (preferred canonical key, e.g. +91) |
KEYWORD | Marketing keyword |
USER_MESSAGE | Optional message |
siteKey | Site key (e.g. pk_live_xxx) for domain validation and lead attribution |
formId | Form ID for lead attribution (multi-form/sites) |
Auto-injected by form.js
These fields are added automatically when using the dynamic form builder:
| Field | Description |
|---|---|
__visitor_id | Visitor ID from tracker.js (localStorage _pg_vid) |
__pg_hp | Honeypot field value (should be empty for real users) |
__pg_tts | Time-to-submit in seconds |
Campaign parameters are also auto-injected as __gclid, __utm_source, __utm_medium, __utm_campaign, __utm_term, __utm_content, __fbclid, __msclkid.
Country Code Input Compatibility
Backend normalizes country code keys and always treats them as COUNTRYCODE.
- Preferred key:
COUNTRYCODE - Also accepted:
countryCode,country_code
Payload sent to Make.com webhook
When Send leads to Make.com is enabled, we forward the enriched lead to your Make.com webhook URL. This is the JSON body your Webhooks – Custom webhook trigger receives. Use it to map fields in Make.com and to call back with lead_public_id / make_send_public_id for CRM and execution linking.
Sample payload (what Make.com receives)
{
"first_name": "Rahul",
"phone_mobile": "9876543210",
"phone_with_country_code": "+91-9876543210",
"email": "rahul@example.com",
"email1": "rahul@example.com",
"lead_source": "WebSite",
"status": "New",
"project_name": "Sunrise Apartments",
"user_location": "Pune",
"refered_by": "https://www.example.com/upcoming-pune/",
"country_code": "+91",
"location": "Pune",
"source_team": "Marketing",
"searched_keyword": "",
"user_agent": "Mozilla/5.0 ...",
"user_ip": "203.0.113.42",
"user_country": "India",
"user_city": "Pune",
"user_timezone": "Asia/Kolkata",
"user_region": "Maharashtra",
"user_region_code": "MH",
"user_creation_datetime": "2026-03-08T10:30:00.000Z",
"is_duplicate": false,
"is_bot_lead": false,
"test_lead": "no",
"hidden_user_message": "",
"dev_environment": "production",
"visitor_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"session_id": "sess_xyz789",
"engagement_score": 45,
"engagement_label": "Warm",
"pages_visited": 5,
"session_duration_seconds": 120,
"return_visits": 1,
"vpn_suspected": 0,
"form_submit_count": 1,
"suspicion_score": 0,
"suspicion_reasons": "",
"time_to_submit": 12,
"related_projects": "Sunset Towers",
"lead_id": 1001,
"lead_public_id": "550e8400-e29b-41d4-a716-446655440000",
"lead_submission_id": 2001,
"lead_submission_public_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"make_send_public_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}Field summary
| Category | Keys | Use in Make.com |
|---|---|---|
| Contact | first_name, email, email1, phone_mobile, phone_with_country_code, country_code | Map to CRM contact |
| Project | project_name, user_location, lead_source, source_team | Map to deal/project |
| Geo | user_country, user_city, user_region, user_region_code, user_timezone | CRM / reporting |
| IDs for callbacks | lead_public_id, lead_submission_public_id, lead_id, lead_submission_id | CRM callback: send back with crm_lead_id |
| Make.com link | make_send_public_id | Make callback: send back with make_execution_url |
| Scoring | engagement_score, engagement_label, pages_visited, session_duration_seconds, return_visits | Lead quality / routing |
| Fraud | suspicion_score, suspicion_reasons, time_to_submit, form_submit_count | Filter or flag leads |
| Context | refered_by, searched_keyword, user_agent, user_ip, related_projects | Attribution |
| Status | is_duplicate, is_follow_up, is_bot_lead, test_lead, dev_environment | Skip or tag in CRM; is_follow_up = same person/project as existing lead (true for follow-up, false for first submission) |
Response
On success, the handler returns a 301 redirect. The redirect target is derived from the Referer header or the DOMAIN field. DOMAIN may be: (1) a full URL (used as-is for base), (2) hostname-only (e.g. example.com) normalized to https://{DOMAIN}, or (3) path-only (e.g. /upcoming-pune/v/) in which case the origin is taken from the request Referer and the path is appended; thank-you is then appended (e.g. https://www.example.com/upcoming-pune/v/thank-you.html). For WebSite source the destination is {base}/thank-you.html; for internal TEAM it is {base}/thank-you-{slug}. When no valid referer or DOMAIN is available, the response is 200 JSON with { message: 'All set!' }.
Storage: The submission page URL (form PAGE_URL, request Referer, or refered_by) is stored in both Leads.Referer and LeadSubmissions.Referer so both tables use the same column name.
Environment Variables
| Variable | Description |
|---|---|
ENVIRONMENT | development or production |
BOT_LEAD_DETECTION | Enable/disable bot detection |
When bot detection is enabled, bot-classified submissions are still stored in D1 for history and investigation; only CRM forwarding is suppressed.