Skip to main content

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"
}
}'
FieldTypeNotes
name, emailstringIdentify the contact.
contactNumber{ countryCode, number }Primary phone.
work_phone_number, whatsapp_contact_number{ countryCode, number }Optional extra numbers.
contactIdstringAttach to an existing contact; skips the matching step.
contactDataobjectExtra fields for the contact being created.
sourceenumSilently dropped if not a known source.
formDataobjectEverything on the lead itself, system and custom. See Custom fields.

Deduplication

The contact is matched on your company's configured unique keyemail 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": { ... } } }.

CombinationWhat happens
enrich onlyThe contact is queued for enrichment. Nothing else.
genie onlyAn AI Genie run starts immediately on the new record.
bothOne 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.

MethodEndpointPurpose
GET/lead-activityTimeline across leads. Filter by lead.
GET/lead-activity/:idOne activity entry.
PATCH/lead-activity/:id/notesEdit the note on an entry.
GET/lead-activity/follow-upScheduled follow-ups.
POST/lead-activity/follow-upSchedule one.
PATCH/lead-activity/follow-up/:idReschedule 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.

MethodEndpointPurpose
GET / POST/segmentList / create.
GET / PUT/segment/:idRead / update.
DELETE/segmentDelete — { "ids": [...] }.
GET/segment/executeResolve 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 (queuedprocessingcompleted / 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

MethodEndpointPurpose
POST/lead/lead-gen-aiStart a generation run.
GET/lead-gen-aiList runs.
GET/lead-gen-ai/:configIdRun detail and generated leads.
DELETE/lead-gen-ai/:configIdDelete a run.

Generated records are flagged aiGenerated: true and carry leadGeneratorConfigId, so you can always separate them from leads your own systems created.