Here’s the complete solution addressing webhook event coverage, contact merge event handling, and polling as a workaround.
Understanding Webhook Event Coverage Limitations:
HubSpot’s webhook system has a significant gap when it comes to contact merge operations. The available webhook event types include:
- contact.propertyChange: Fires when properties are updated, but NOT during merges
- contact.creation: Fires for new contacts only
- contact.deletion: Fires when contacts are deleted, INCLUDING the secondary contact in a merge
The critical issue is that contact.deletion fires for both manual deletions and merge operations, but the webhook payload doesn’t distinguish between these scenarios or provide merge context (like which contact it was merged into). This means webhooks alone cannot reliably detect and properly handle contact merges.
Contact Merge Event Handling - What Actually Happens:
When two contacts are merged in HubSpot:
- Primary contact retains its ID and receives merged data from secondary contact
- Secondary contact is deleted (soft delete with audit trail)
- Properties from secondary are selectively merged into primary based on merge rules
- All associations (deals, tickets, activities) are transferred to primary contact
- Audit log records the merge event with both contact IDs and timestamp
Your webhook subscription to contact.propertyChange will never fire because HubSpot doesn’t treat merge as a property change event - it’s a distinct operation type at the platform level.
Polling as Workaround - Implementation Strategy:
Since webhooks don’t provide adequate merge event coverage, implement a polling-based solution using HubSpot’s audit log API:
// Poll for merge events every 5 minutes
GET /crm/v3/objects/contacts/audit
params: {
eventType: "MERGE",
occurredAfter: lastCheckTimestamp,
limit: 100
}
Detailed Polling Implementation:
Step 1 - Set Up Polling Job:
- Create scheduled job (cron or workflow-based) that runs every 3-5 minutes
- Store last successful poll timestamp in persistent storage
- Query audit log for merge events since last timestamp
- Process each merge event sequentially
Step 2 - Parse Merge Event Data:
Audit log merge events contain:
{
"eventType": "MERGE",
"objectId": "12345", // Primary (surviving) contact
"mergedObjectIds": ["67890"], // Secondary (deleted) contact
"occurredAt": "2025-06-08T15:30:00Z",
"userId": "user123"
}
Step 3 - Implement Sync Logic for Downstream Systems:
For each detected merge, propagate to all integrated systems:
// Pseudocode for merge propagation:
1. Fetch full primary contact data from HubSpot
2. For each downstream system:
a. Check if secondary contact ID exists
b. Update all foreign key references from secondary to primary
c. Merge any system-specific data from secondary record
d. Soft-delete or archive secondary record
e. Log successful sync
3. Update last processed timestamp
Handling Different Downstream System Patterns:
External CRM Sync:
- Query for all records with foreign key = deleted contact ID
- Update foreign key to primary contact ID
- Merge any custom fields or notes from deleted record
- Mark deleted record as “merged” with reference to primary
Support Ticketing System:
- Reassign all tickets from secondary contact to primary contact
- Update ticket history to show merge event
- Consolidate contact information on primary record
Data Warehouse:
- Update fact tables with new contact ID for historical records
- Create merge tracking table: (deleted_id, primary_id, merge_timestamp)
- Update dimension tables to mark secondary contact as merged
- Maintain audit trail for reporting and analysis
Error Handling and Recovery:
- Log each merge event processed with timestamp and both contact IDs
- Implement retry logic for failed downstream syncs (exponential backoff)
- Create dead letter queue for merge events that fail after multiple retries
- Alert on sync failures that exceed threshold (e.g., 3+ consecutive failures)
- Implement manual reconciliation process for failed merges
Monitoring and Validation:
- Track polling job execution (success rate, duration, events processed)
- Monitor lag between merge occurrence and downstream sync completion
- Create dashboard showing: pending merges, sync status by system, error rate
- Implement periodic validation: compare HubSpot contact IDs with downstream system IDs
- Alert on orphaned records (downstream records pointing to deleted HubSpot contact IDs)
Optimization Strategies:
Reduce Polling Frequency Impact:
- Use lastModifiedDate filter to minimize API calls
- Implement cursor-based pagination for large result sets
- Cache audit log responses briefly to handle job restarts
- Batch process multiple merge events together for downstream syncs
Minimize Sync Delay:
- Run polling job every 2-3 minutes for near-real-time sync (balance with API limits)
- Implement priority queue: process recent merges before older ones
- Parallelize downstream system updates where possible
- Use bulk update APIs in downstream systems when available
Alternative Approach - Hybrid Solution:
Combine polling with deletion webhook for faster detection:
- Subscribe to contact.deletion webhook
- When deletion webhook fires, immediately query audit log for that contact ID
- Check if deletion was part of merge operation
- If merge detected, trigger immediate sync (don’t wait for polling cycle)
- Polling job still runs as backup to catch any missed events
This hybrid approach reduces average sync delay from 3-5 minutes (pure polling) to under 30 seconds while maintaining reliability.
Expected Performance:
- Merge detection latency: 2-5 minutes (polling) or 10-30 seconds (hybrid)
- Downstream sync completion: 1-3 minutes after detection
- Total end-to-end delay: 3-8 minutes (acceptable for most use cases)
- Reliability: 99.9%+ with proper error handling and retry logic
The key insight is accepting that HubSpot’s webhook system has inherent limitations for merge events and designing a robust polling-based solution that provides reliable eventual consistency across all integrated systems.
This draft is based on general HubSpot knowledge. It has not been verified against your specific version and environment. Practitioners: verify the steps and share your experience below.