Leads
A lead is an enquiry. It always points at a contact — the person — and carries the pipeline state: stage, status, priority, source, assigned team and member, attribution, and any custom fields.
Creating a lead creates or reuses the contact behind it automatically. You never have to create the contact first.
Create a lead
curl -X POST https://api.leadx.in/api/key/protected/lead \
-H "apikey: $KEY" -H "Content-Type: application/json" \
-d '{
"name": "Ada Lovelace",
"email": "ada@example.com",
"contactNumber": { "countryCode": "91", "number": "9876543210" },
"source": "Website",
"formData": {
"stage": "New",
"status": "Hot",
"priority": "High",
"notes": "Asked about the enterprise tier",
"utmSource": "google",
"utmCampaign": "q3-brand"
}
}'
| Field | Type | Notes |
|---|---|---|
name, email | string | Identify the contact. |
contactNumber | { countryCode, number } | Primary phone. |
work_phone_number, whatsapp_contact_number | { countryCode, number } | Optional extra numbers. |
contactId | string | Attach to an existing contact; skips the matching step. |
contactData | object | Extra fields for the contact being created. |
source | enum | Silently dropped if not a known source. |
formData | object | Everything on the lead itself, system and custom. See Custom fields. |
Deduplication
The contact is matched on your company's configured unique key — email by default, settable per company. A matching contact is reused and a new lead attached to it.
Separately, if formData.leadId is supplied and a lead already carries that external id, the existing lead is returned unchanged. That makes creation idempotent for syncs:
{ "formData": { "leadId": "crm-8842" }, "email": "ada@example.com" }
Resending the same leadId will not create a duplicate.
Create with enrichment and outreach in one call
Both create endpoints accept an automation block, so "add this lead, enrich it, and start reaching out" is one request instead of three:
{
"email": "ada@example.com",
"formData": { "stage": "New" },
"enrich": true,
"genie": {
"prompt": "Introduce our analytics platform and ask for a 15-minute call",
"tone": "friendly",
"draft": true
}
}
Nested form works too: { "automation": { "enrich": true, "genie": { ... } } }.
| Combination | What happens |
|---|---|
enrich only | The contact is queued for enrichment. Nothing else. |
genie only | An AI Genie run starts immediately on the new record. |
| both | One Genie run that enriches first and holds outreach until enrichment reports back. |
genie: true on its own is valid — the run plans from your company profile and catalogue when no prompt is given. Set draft: true to have messages created for approval instead of sent.
:::note Automation is best-effort
The record is already saved by the time automation runs, so a Genie outage is reported in an automation key on the response rather than failing the create. Always check automation when you asked for it.
:::
On the bulk endpoint the automation block is read once from the top level and applied to the whole batch — 100 leads with Genie on start one campaign covering 100 people, not 100 campaigns.
List and filter
curl -G https://api.leadx.in/api/key/protected/lead \
-H "apikey: $KEY" \
--data-urlencode 'p=1' \
--data-urlencode 'n=50' \
--data-urlencode 'filters={"stage":["Qualified","Negotiation"],"status":"Hot"}' \
--data-urlencode 'sortBy=createdAt' \
--data-urlencode 'sortOrder=desc'
Sort by related data without joins — contact.name, contact.email, team.name, teamMember.name, createdBy.name. See Pagination & sorting.
For a stable full export, use cursor pagination.
Update
Single lead:
curl -X PUT https://api.leadx.in/api/key/protected/lead/665f... \
-H "apikey: $KEY" -H "Content-Type: application/json" \
-d '{ "formData": { "stage": "Won" }, "tags": ["665a..."] }'
Many at once — see Bulk operations:
{ "ids": ["665f...", "6660..."], "formData": { "stage": "Contacted" } }
Delete
DELETE /lead with { "ids": [...] }. Leads are soft-deleted (isDeleted: true) and disappear from list results. There is no DELETE /lead/:id.
Activity and follow-ups
Every stage change, message and note lands on the lead's timeline.
| Method | Endpoint | Purpose |
|---|---|---|
GET | /lead-activity | Timeline across leads. Filter by lead. |
GET | /lead-activity/:id | One activity entry. |
PATCH | /lead-activity/:id/notes | Edit the note on an entry. |
GET | /lead-activity/follow-up | Scheduled follow-ups. |
POST | /lead-activity/follow-up | Schedule one. |
PATCH | /lead-activity/follow-up/:id | Reschedule or complete. |
Segments
A segment is a saved advanced filter. Campaigns target segments rather than fixed id lists, so the audience re-resolves at send time.
| Method | Endpoint | Purpose |
|---|---|---|
GET / POST | /segment | List / create. |
GET / PUT | /segment/:id | Read / update. |
DELETE | /segment | Delete — { "ids": [...] }. |
GET | /segment/execute | Resolve a segment to a count without sending anything. |
Use /segment/execute to size an audience before committing to a campaign.
Enrichment
curl -X POST https://api.leadx.in/api/key/protected/enrich/lead \
-H "apikey: $KEY" -H "Content-Type: application/json" \
-d '{ "ids": ["665f..."] }'
Enrichment is asynchronous. Track it on the lead's enrichmentStatus (queued → processing → completed / failed), with enrichmentRequestedAt, enrichmentDoneAt, enrichmentCount and enrichmentHistory alongside.
A contact already being enriched will refuse a second concurrent request — which is why combining enrich with genie uses one run rather than two.
AI lead generation
| Method | Endpoint | Purpose |
|---|---|---|
POST | /lead/lead-gen-ai | Start a generation run. |
GET | /lead-gen-ai | List runs. |
GET | /lead-gen-ai/:configId | Run detail and generated leads. |
DELETE | /lead-gen-ai/:configId | Delete a run. |
Generated records are flagged aiGenerated: true and carry leadGeneratorConfigId, so you can always separate them from leads your own systems created.