Skip to content

Lead Capture API

Endpoint

MethodPathDescription
POST/lead.capturePrimary — accepts lead form submissions
POST/leadDeprecated — 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.capture route. The legacy /lead and / paths remain functional but will be removed after 2026-06-01. Update your integrations to use POST /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):

  1. The key is validated against the Integrations table.
  2. If invalid → 401 Unauthorized.
  3. If valid, domain validation is skipped and the lead is stored. Optional siteKey and formId in 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:

  1. The body must include both siteKey and formId. If either is missing or empty → 403 Forbidden with a clear error message.
  2. The request domain (from Origin or Referer) is validated against SiteDomains for that site. If not allowed → 403 Forbidden.
  3. Site and form must be enabled in the dashboard; otherwise → 403 Forbidden.
  4. siteKey and formId are 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

  1. Blocked IP check — If the request IP is in BlockedIPs, reject (record in BlockedIPLeads, return success/redirect; no further steps).
  2. Accepts POST requests with form data (JSON, application/x-www-form-urlencoded, multipart/form-data, or text/plain)
  3. Extracts lead fields: name, email, phone, project, location, team
  4. Enriches with geolocation data from Cloudflare headers
  5. Looks up visitor's engagement score from tracking sessions
  6. Calculates suspicion score (honeypot, time-to-submit, form velocity)
  7. Finds related projects the visitor previously inquired about
  8. Checks for bot leads (when BOT_LEAD_DETECTION is enabled)
  9. Resolves canonical lead by identity + project (visitor_id > email > phone > IP)
  10. Writes every submit into LeadSubmissions history table
  11. Forwards the enriched payload to a Make.com webhook for new primary leads
  12. Stores canonical lead in D1 Leads table
  13. Returns a 301 redirect to a thank-you page

Expected Fields

FieldDescription
FIRSTNAMELead's first name
EMAILLead's email address
PHONELead's phone number
PROJECTReal estate project name
LOCATIONCity/location
TEAMSource team identifier
DOMAINSource 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/.
SOURCELead source (WebSite, FB, 99)
COUNTRYCODECountry dial code (preferred canonical key, e.g. +91)
KEYWORDMarketing keyword
USER_MESSAGEOptional message
siteKeySite key (e.g. pk_live_xxx) for domain validation and lead attribution
formIdForm ID for lead attribution (multi-form/sites)

Auto-injected by form.js

These fields are added automatically when using the dynamic form builder:

FieldDescription
__visitor_idVisitor ID from tracker.js (localStorage _pg_vid)
__pg_hpHoneypot field value (should be empty for real users)
__pg_ttsTime-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)

json
{
  "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

CategoryKeysUse in Make.com
Contactfirst_name, email, email1, phone_mobile, phone_with_country_code, country_codeMap to CRM contact
Projectproject_name, user_location, lead_source, source_teamMap to deal/project
Geouser_country, user_city, user_region, user_region_code, user_timezoneCRM / reporting
IDs for callbackslead_public_id, lead_submission_public_id, lead_id, lead_submission_idCRM callback: send back with crm_lead_id
Make.com linkmake_send_public_idMake callback: send back with make_execution_url
Scoringengagement_score, engagement_label, pages_visited, session_duration_seconds, return_visitsLead quality / routing
Fraudsuspicion_score, suspicion_reasons, time_to_submit, form_submit_countFilter or flag leads
Contextrefered_by, searched_keyword, user_agent, user_ip, related_projectsAttribution
Statusis_duplicate, is_follow_up, is_bot_lead, test_lead, dev_environmentSkip 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

VariableDescription
ENVIRONMENTdevelopment or production
BOT_LEAD_DETECTIONEnable/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.