Submitting Leads
How to submit leads via the API and understand the engine processing flow.
Submitting Leads
Leads enter LeadSail through the submission API endpoint. When a lead is submitted, it goes through a multi-step processing pipeline.
API Submission
Submit a lead using the POST /api/v1/leads/submit endpoint:
curl -X POST https://api.leadsail.app/api/v1/leads/submit \
-H "X-API-Key: your_api_key" \
-H "Content-Type: application/json" \
-d '{
"campaignId": "cmp_abc123",
"firstName": "Jane",
"lastName": "Smith",
"email": "jane@example.com",
"phone": "+15559876543",
"address": {
"street": "123 Main St",
"city": "Los Angeles",
"state": "CA",
"zip": "90210"
}
}'Request Body
The schema uses strict validation — unknown fields are rejected.
Required Fields
| Field | Type | Description |
|---|---|---|
campaignId | string | The campaign to submit the lead to |
firstName | string | Contact first name (trimmed, title-cased). Required for new leads, optional when leadId is provided |
lastName | string | Contact last name (trimmed, title-cased). Required for new leads, optional when leadId is provided |
email | string | Email address (lowercased). Required for new leads, optional when leadId is provided |
phone | string | Phone number, minimum 10 digits. Required for new leads, optional when leadId is provided |
Optional Fields
| Field | Type | Description |
|---|---|---|
leadId | string | Existing lead ID. When provided, the lead's existing data is used and the lead is re-submitted through the qualification engine. Contact fields become optional |
address | object | See Address below |
dateOfBirth | string | Format: YYYY-MM-DD |
country | string | 2-letter uppercase ISO code (e.g., US, CA) |
questionnaire | object or array | See Questionnaire below. Defaults to {} |
customFields | object | Arbitrary key-value pairs |
attribution | object | See Attribution below |
compliance | object | See Compliance below |
files | array | See Files below |
isTest | boolean | Flag as a test record (skips deduplication and pixel firing) |
timezone | string | IANA timezone string (e.g., America/New_York) |
notes | string | Free-text notes |
Address
All address fields are required when address is provided, except unit and country.
| Field | Type | Description |
|---|---|---|
street | string | Street address |
unit | string | Unit/apartment number (optional) |
city | string | City |
state | string | 2-letter uppercase state code (e.g., CA, NY) |
zip | string | 5 or 9 digit ZIP code (e.g., 90210 or 90210-1234) |
country | string | 2-letter uppercase ISO code. Defaults to US (optional) |
Questionnaire
Accepts two formats:
Object format — keys are mapped to question labels via your field library. If a key doesn't exist in the library, the key name is used as the label.
{
"dob": "1990-01-15",
"injury_type": "car_accident"
}Array format — you provide the question labels directly.
[
{ "key": "dob", "question": "Date of Birth", "answer": "1990-01-15" },
{ "key": "injury_type", "question": "Type of Injury", "answer": "car_accident" }
]| Field | Type | Description |
|---|---|---|
key | string | Field identifier (required) |
question | string | Display label for the field (required) |
answer | any | The answer value (required) |
Attribution
Track marketing source and ad platform data. The attribution structure mirrors the internal Lead type directly.
| Field | Type | Description |
|---|---|---|
params | object | All tracking parameters — UTMs, custom URL params, etc. Stored as-is on the lead. Defaults to {} |
captureUrl | string | URL where the lead was captured |
referrer | string | Referring URL |
ipAddress | string | Lead's IP address (auto-captured from request if not provided) |
userAgent | string | Lead's browser user agent (auto-captured from request if not provided) |
gclid | string | Google Ads click ID |
gbraid | string | Google Ads app click ID |
wbraid | string | Google Ads web-to-app click ID |
fbclid | string | Facebook click ID |
fbc | string | Facebook browser cookie |
fbp | string | Facebook pixel cookie |
ttclid | string | TikTok click ID |
ttp | string | TikTok pixel cookie |
msclkid | string | Microsoft Ads click ID |
tblci | string | Taboola click ID |
adPlatform | string | Ad platform name (auto-detected from click IDs if not provided) |
capturePlatform | string | Capture platform name |
Example attribution object:
{
"params": {
"utm_source": "facebook",
"utm_medium": "paid_social",
"utm_campaign": "spring_2024",
"custom_tracking_id": "abc123"
},
"fbclid": "fbclid_xyz789",
"captureUrl": "https://example.com/landing"
}Compliance
TCPA consent and verification data.
| Field | Type | Description |
|---|---|---|
tcpaConsent | boolean | Whether TCPA consent was given |
tcpaConsentTimestamp | string | ISO 8601 datetime of consent |
tcpaConsentMethod | string | One of: checkbox, signature, sms_optin, voice_recording |
tcpaLanguage | string | TCPA consent language shown to the lead |
trustedFormCertId | string | TrustedForm certificate ID |
trustedFormCertUrl | string | TrustedForm certificate URL |
jornayaLeadId | string | Jornaya LeadiD token |
privacyPolicyUrl | string | Privacy policy URL shown to the lead |
termsUrl | string | Terms of service URL shown to the lead |
customFields | object | Additional compliance key-value data |
Files
Attach file references to the lead.
| Field | Type | Description |
|---|---|---|
sourceKey | string | Identifier for the file source (required) |
fileUrl | string | URL where the file is hosted (required) |
type | string | File type/category |
metadata | object | Additional file metadata |
Processing Pipeline
When a lead is submitted, the qualification engine processes it through these steps:
- Routing Rules — Evaluate which campaigns match the lead's data
- Disqualification Conditionals — Check for hard DQ conditions (e.g., excluded states)
- Dynamic Keys — Apply dynamic field transformations
- Required Fields — Verify all required fields are present
- Field Qualification — AI-powered validation of field values
- Payload Building — Construct the delivery payload
- Delivery — Send to the campaign's endpoints
- Fallback — If rejected, try fallback campaigns
Response
A successful submission returns:
{
"success": true,
"leadId": "lead_abc123",
"submissionId": "sub_def456",
"submissionNumber": 1,
"message": "Lead submitted for processing",
"timestamp": "2024-01-15T10:30:00.000Z"
}The lead will be processed asynchronously. Use the Get Lead Details endpoint to check the status.