Deduplication
Every POST /leads request runs automatic duplicate detection before inserting. This prevents polluting your database with duplicate entries from different sources.
Detection priority
Section titled “Detection priority”| Priority | Criterion | Confidence | Action |
|---|---|---|---|
| 1 | email match (case-insensitive) | High | Merge into existing |
| 2 | source_id exact match | High | Merge into existing |
| 3 | phone match + name similarity > 80% | Medium | Merge into existing (similarity uses normalized string comparison — “Acme Corp” and “ACME Corporation” may not match) |
| 4 | name + city match (case-insensitive) | Low | Create new, flag with potential_duplicate_id |
Merge strategy: Last Touch Wins
Section titled “Merge strategy: Last Touch Wins”When a duplicate is detected at priority 1–3:
- Non-empty fields from the incoming request overwrite the existing lead.
- Fields not present in the request are left unchanged.
last_interaction_atis always bumped — even if no field changed.
This models “freshness of interest”: every new push from a source registers as a new interaction.
Response on duplicate
Section titled “Response on duplicate”{ "id": "cl_existing...", "message": "Duplicate detected, existing lead updated", "created_at": 1703520000, "duplicate": true}HTTP status is 200 OK (not 201) on a merge.
Potential duplicate (low confidence)
Section titled “Potential duplicate (low confidence)”On a name+city match only, a new lead is created but the response includes a hint:
{ "id": "cl_new...", "potential_duplicate_id": "cl_similar...", "message": "Lead created successfully"}You can review these in the dashboard: navigate to Leads, search for the potential_duplicate_id, and manually merge or dismiss.
updated_at vs last_interaction_at
Section titled “updated_at vs last_interaction_at”| Field | Updates when |
|---|---|
updated_at | A structural field is modified (name, email, city, etc.) |
last_interaction_at | Any push arrives — including merges with no field changes |
Use sort_by=last_interaction_at&sort_order=DESC to find the leads with the most recent activity, independent of data changes.