Duplicate Detection
Where to find it
Settings > Entities > [Entity] > Fields
Duplicate detection: one rule for every lead source
Duplicate detection lives on the entity. Once you configure it, every lead arriving in that entity (whether through a CSV import, the API, or a public form) runs through the same matching logic.
You'll find the controls under Settings → Entities → [your entity] → Fields. They live directly in the Fields tab.
Quick start (60 seconds)
- Open the Fields tab on the entity you want to dedupe.
- Click Edit Field on the field you want to use as the identity key (e.g. Email or External ID), switch to the Rules tab in the drawer, and check "🔑 Use as match key for duplicate detection". Save the field.
- Repeat for any fallback fields (e.g. an external ID first, email as a backup). When two or more keys are configured, a priority strip appears at the top of the Fields tab. Use the ↑ / ↓ arrows to reorder.
- Pick the action per source. What should happen when a match
is found is now decided where the lead arrives:
- Form Builder → Settings → Submission
- Lead Import → Mapping step
- API →
dedup_modein the JSON payload
Every change saves immediately. The default action everywhere is Skip (existing lead untouched). The entity itself only configures the identity (which fields are keys); behaviour belongs to the source, where the operator knows whether they're refreshing data, re-uploading idempotently, or running a funnel that should always create new leads.
What you configure
Identity lives on the entity. Behaviour lives on the source.
The entity defines which fields make up a lead's identity (the "match keys"). The source (Form, CSV import, or API call) decides what to do when an incoming lead matches that identity.
Match keys (entity-level)
To mark a field as a match key, edit the field (click Edit Field on its row) and check the 🔑 Use as match key for duplicate detection option in the Rules tab. The toggle is disabled with an explanation if the field type isn't eligible (encrypted, multi-select, location, boolean, file).
When two or more fields are marked, a compact priority strip appears at the top of the Fields tab. Click the ↑ / ↓ arrows on each chip to reorder.
Action (per-source)
Each source picks one of three behaviours from its own dropdown:
| Action | Behavior |
|---|---|
| Skip duplicates (default) | Existing lead is kept untouched, the new row is dropped. Best for idempotent re-uploads. |
| Update existing lead | Mapped fields on the existing lead are overwritten. Assignee, status, and unmapped fields stay. The activity feed records the change. Best for syncing data from another source. |
| Off, always create | Bypass dedup entirely for this source. Best for funnel forms (lead nurture) where every submission must create a new lead. |
Where each source picks its action:
- Form: Form Builder → Settings → Submission → Duplicate detection
- CSV import: Import drawer → Mapping step → Duplicate detection
- API: top-level
dedup_modefield in the request JSON
When no fields are marked as match keys, duplicate detection is off and every incoming lead is created. The per-source pickers have no effect.
The matcher tries the keys in priority order; the first key whose inbound value is non-empty AND yields a single match wins. If a key gives multiple matches, that's a conflict (recorded but not auto-merged). The matcher does NOT fall back to a lower-priority key.
Typical setups:
- Just email. Simple, works for most lead-capture forms.
- External ID first, email as fallback. Common when integrating with another CRM that has its own ID. The external ID is more reliable but might not always be sent.
- Customer number then phone. For phone-driven flows.
Eligibility: which fields can be match keys
A field can be a match key if it's a plain scalar value that you own. The picker hides anything else:
- Encrypted fields (matching against ciphertext is not supported)
- Multi-select / checkbox-group (array values aren't an identity)
- Location fields (associative addresses, not a single key)
- Boolean and file fields
Eligible types: text, long text, number, date, single-select, plus text specializations like email, phone, URL.
The four outcomes
For every inbound lead the matcher returns one of:
- created. No match found (or the inbound's match values were all empty). Lead is created normally.
- updated. Exactly one match. Mapped fields are written to the existing lead.
- skipped. Exactly one match, action = Skip; or action = Update but every value already matched (no fields changed → no-op).
- conflict. Two or more matches at the highest non-empty priority. Recorded for the post-import report; no lead created or updated.
What does NOT happen on update
By design, an update does not re-fire the entity's automations or distribution. Updates are data refreshes, not state changes. If you need automations to react to specific field changes, a future "on-update trigger" feature will cover that. For now, only create fires the new-lead automation chain.
Behaviour by source
Each source has its own three-way picker (Skip / Update / Off) with Skip as the safe default. Match keys are always entity-level: they're the identity definition, not a per-call setting.
-
CSV import (Mapping step → Duplicate detection): when the picker resolves to Skip or Update and the entity has match keys, the Start Import button becomes Preview Import. You'll see how many rows will be created, updated, skipped, or hit conflicts before any DB write.
-
API intake (
dedup_modein the JSON payload; see.agents/docs/API.md): the response payload includes adecisionfield (created/updated/skipped/conflict) pluslead_id,changed_fields,matches, andmatched_byas appropriate. Existing API clients reading onlylead_idkeep working. -
Form submissions (Form Builder → Settings → Submission → Duplicate detection): pick Skip for re-capture forms, Update for profile-refresh forms, Off for funnel / lead-nurture forms.