Contact Deduplicated Event
Triggered when duplicate contacts are merged due to LID (Local Identifier) to PN (Phone Number) mapping discovery. This happens when WhatsApp reveals that a LID belongs to a phone number that already exists as a separate contact.
Event Type
contact-deduplicated
Payload Structure
{
"event": "contact-deduplicated",
"instanceId": "instance-uuid",
"data": {
"keptContactId": "contact-uuid-kept",
"deletedContactId": "contact-uuid-deleted",
"lid": "LID123@lid",
"pn": "[email protected]",
"messagesReassigned": 15,
"timestamp": "2024-12-14T12:00:00.000Z"
}
}
Data Fields
| Field | Type | Description |
|---|---|---|
keptContactId | string | ID of the contact that was kept (merged into) |
deletedContactId | string | ID of the contact that was deleted (merged from) |
lid | string | The LID that triggered the deduplication |
pn | string | The phone number JID associated with the LID |
messagesReassigned | number | undefined | Number of messages reassigned to the kept contact |
timestamp | string | ISO 8601 timestamp of the deduplication |
Why Deduplication Happens
WhatsApp uses two types of identifiers:
- JID (Phone Number): Traditional identifier like
[email protected] - LID (Local ID): Privacy-focused identifier that hides the phone number
When you first interact with a contact, you might only see their LID. Later, WhatsApp may reveal that this LID maps to a phone number. If you already have a contact with that phone number, the system merges them to avoid duplicates.
Before deduplication:
├── Contact A (JID: [email protected])
└── Contact B (LID: LID123@lid)
WhatsApp reveals: LID123 = 5511999999999
After deduplication:
└── Contact A (JID: [email protected], LID: LID123@lid)
└── All messages from Contact B reassigned here
Example: Handling Deduplication
app.post('/webhook', async (req, res) => {
const { event, data } = req.body;
if (event === 'contact-deduplicated') {
const { keptContactId, deletedContactId, messagesReassigned } = data;
// Update your local database to reflect the merge
await db.transaction(async (tx) => {
// Reassign any local references from deleted to kept contact
await tx.conversations.update(
{ contactId: deletedContactId },
{ contactId: keptContactId }
);
await tx.messages.update(
{ contactId: deletedContactId },
{ contactId: keptContactId }
);
// Delete the duplicate contact record
await tx.contacts.delete({ externalId: deletedContactId });
});
console.log(
`Merged contact ${deletedContactId} into ${keptContactId}. ` +
`${messagesReassigned || 0} messages reassigned.`
);
}
res.status(200).send('OK');
});
Important Considerations
Update Your References
When you receive this event, make sure to update any references to deletedContactId in your system to point to keptContactId instead. The deleted contact no longer exists in Zapy.
- Message History: All messages from the deleted contact are automatically reassigned to the kept contact in Zapy
- Chat Continuity: The conversation continues seamlessly under the kept contact
- Data Integrity: The
messagesReassignedfield helps you verify the merge was complete