Merge duplicates code errors

I was using the default merge duplicates code and updated the property that I need to merge contacts by which is “rim_no” (a unique customer identifyer in our database). I have set up the API key in “Secrets” but after a lot of trouble shooting, I just can’t figure it out. Anyone have any ideas?

Here’s the code:

Spoiler

/**
* Searches for another contact with the same value of DEDUPE_PROPERTY.
* - If no matches are found, nothing happens
* - If one match is found, the enrolled contact is merged into the matching contact
* - If more than one match is found, the action fails
*/

const DEDUPE_PROPERTY = ‘rim_no’;

const hubspot = require(‘@hubspot/api-client’);

exports.main = (event, callback) => {
// Make sure to add your API key under “Secrets” above.
const hubspotClient = new hubspot.Client({
accessToken: process.env.SECRET_NAME,
});

hubspotClient.crm.contacts.basicApi
.getById(event.object.objectId, [DEDUPE_PROPERTY])
.then(contactResult => {
let dedupePropValue = contactResult.body.properties[DEDUPE_PROPERTY];

// console.log(`Looking for duplicates based on ${DEDUPE_PROPERTY} = ${dedupePropValue}`);
hubspotClient.crm.contacts.searchApi
.doSearch({
filterGroups: [{
filters: [{
propertyName: DEDUPE_PROPERTY,
operator: ‘EQ’,
value: dedupePropValue
}]
}]
})
.then(searchResults => {
let idsToMerge = searchResults.body.results
.map(object => object.id)
.filter(vid => Number(vid) !== Number(event.object.objectId));

if (idsToMerge.length == 0) {
console.log(‘No matching contact, nothing to merge’);
return;
} else if (idsToMerge.length > 1) {
console.log(`Found multiple potential contact IDs ${idsToMerge.join(', ')} to merge`);
throw new Error(“Ambiguous merge; more than one matching contact”);
}

let idToMerge = idsToMerge[0];
console.log(`Merging enrolled contact id=${event.object.objectId} into contact id=${idToMerge}`);
hubspotClient
.apiRequest({
method: ‘PATCH’,
path: `/crm/v3/objects/contacts/IDOFSECONDARY`,
body: {
“properties”: {
“dupe”: “duplicate”
}

}
})
.then(mergeResult => {
console.log(‘Contacts merged!’);
});
});
});
};

Here’s the error log:

Spoiler

WARNING: The logs for this function have exceeded the 4KB limit.
...
ontrol-allow-credentials\":\"false\",\"cf-cache-status\":\"DYNAMIC\",\"cf-ray\":\"8a848b6a5f2887ac-IAD\",\"connection\":\"close\",\"content-length\":\"299\",\"content-type\":\"application/json;charset=utf-8\",\"date\":\"Wed, 24 Jul 2024 14:19:20 GMT\",\"nel\":\"{\\\"success_fraction\\\":0.01,\\\"report_to\\\":\\\"cf-nel\\\",\\\"max_age\\\":604800}\",\"report-to\":\"{\\\"endpoints\\\":[{\\\"url\\\":\\\"https:\\\\/\\\\/a.nel.cloudflare.com\\\\/report\\\\/v4?s=R2f5tgv4JVSRn%2FjWX2DmhksTGsVDCYh2KRLzkRPRHy6lR2%2BLNg0brV8fmXULq03p%2F9Sy8CyIfBqFoAT0dCrNOaRLH3%2FlYCa8AbHa5WufRcjIuF7wPZB%2FR3hfQN5raPot\\\"}],\\\"group\\\":\\\"cf-nel\\\",\\\"max_age\\\":604800}\",\"server\":\"cloudflare\",\"strict-transport-security\":\"max-age=31536000; includeSubDomains; preload\",\"vary\":\"origin, Accept-Encoding\",\"x-content-type-options\":\"nosniff\",\"x-envoy-upstream-service-time\":\"4\",\"x-evy-trace-listener\":\"listener_https\",\"x-evy-trace-route-configuration\":\"listener_https/all\",\"x-evy-trace-route-service-name\":\"envoyset-translator\",\"x-evy-trace-served-by-pod\":\"iad02/hubapi-td/envoy-proxy-7dd59b876-mtwxl\",\"x-evy-trace-virtual-host\":\"all\",\"x-hubspot-auth-failure\":\"401 Unauthorized\",\"x-hubspot-correlation-id\":\"5cb63b81-60bd-445e-93de-b503bbdacda5\",\"x-request-id\":\"5cb63b81-60bd-445e-93de-b503bbdacda5\"}"," at BasicApiResponseProcessor.<anonymous> (/opt/nodejs/node_modules/@hubspot/api-client/lib/codegen/crm/contacts/apis/BasicApi.js:227:23)"," at Generator.next (<anonymous>)"," at fulfilled (/opt/nodejs/node_modules/@hubspot/api-client/lib/codegen/crm/contacts/apis/BasicApi.js:5:58)"," at processTicksAndRejections (node:internal/process/task_queues:96:5)"]},"promise":{},"stack":["Runtime.UnhandledPromiseRejection: Error: HTTP-Code: 401","Message: An error occurred.","Body: {\"status\":\"error\",\"message\":\"Authentication credentials not found. This API supports OAuth 2.0 authentication and you can find more details at https://developers.hubspot.com/docs/methods/auth/oauth-overview\",\"correlationId\":\"5cb63b81-60bd-445e-93de-b503bbdacda5\",\"category\":\"INVALID_AUTHENTICATION\"}","Headers: {\"access-control-allow-credentials\":\"false\",\"cf-cache-status\":\"DYNAMIC\",\"cf-ray\":\"8a848b6a5f2887ac-IAD\",\"connection\":\"close\",\"content-length\":\"299\",\"content-type\":\"application/json;charset=utf-8\",\"date\":\"Wed, 24 Jul 2024 14:19:20 GMT\",\"nel\":\"{\\\"success_fraction\\\":0.01,\\\"report_to\\\":\\\"cf-nel\\\",\\\"max_age\\\":604800}\",\"report-to\":\"{\\\"endpoints\\\":[{\\\"url\\\":\\\"https:\\\\/\\\\/a.nel.cloudflare.com\\\\/report\\\\/v4?s=R2f5tgv4JVSRn%2FjWX2DmhksTGsVDCYh2KRLzkRPRHy6lR2%2BLNg0brV8fmXULq03p%2F9Sy8CyIfBqFoAT0dCrNOaRLH3%2FlYCa8AbHa5WufRcjIuF7wPZB%2FR3hfQN5raPot\\\"}],\\\"group\\\":\\\"cf-nel\\\",\\\"max_age\\\":604800}\",\"server\":\"cloudflare\",\"strict-transport-security\":\"max-age=31536000; includeSubDomains; preload\",\"vary\":\"origin, Accept-Encoding\",\"x-content-type-options\":\"nosniff\",\"x-envoy-upstream-service-time\":\"4\",\"x-evy-trace-listener\":\"listener_https\",\"x-evy-trace-route-configuration\":\"listener_https/all\",\"x-evy-trace-route-service-name\":\"envoyset-translator\",\"x-evy-trace-served-by-pod\":\"iad02/hubapi-td/envoy-proxy-7dd59b876-mtwxl\",\"x-evy-trace-virtual-host\":\"all\",\"x-hubspot-auth-failure\":\"401 Unauthorized\",\"x-hubspot-correlation-id\":\"5cb63b81-60bd-445e-93de-b503bbdacda5\",\"x-request-id\":\"5cb63b81-60bd-445e-93de-b503bbdacda5\"}"," at process.<anonymous> (file:///var/runtime/index.mjs:1276:17)"," at process.emit (node:events:513:28)"," at emit (node:internal/process/promises:140:20)"," at processPromiseRejections (node:internal/process/promises:274:27)"," at processTicksAndRejections (node:internal/process/task_queues:97:32)"]}
Unknown application error occurred
Runtime.Unknown

Memory: 75/128 MB
Runtime: 1163.15 ms

Hi @CSteinbach and welcome to the Community!
We are delighted to have you join us!
Thank you for asking the Community and for sharing the code!
Since this a public space, please make sure that there is no confidential information.
I have found this similar thread where the solution from @GJakobTriad on this post “Custom code and Company deduplication” might help you.
Also, I’d like to invite a couple of subject matter experts and Community Members to this conversation: Hi @andreaska, @himanshurauthan and @zach_threadint do you have troubleshooting steps to help @CSteinbach, please?
Thank you very much and have a great day!
Best,
Bérangère

I have continued troubleshooting using the resources provided, but I’m still running into errors.

Here the updated code:

/**
 * Searches for another contact with the same value of DEDUPE_PROPERTY.
 * - If no matches are found, nothing happens
 * - If one match is found, the enrolled contact is merged into the matching contact
 * - If more than one match is found, the action fails
 */

const DEDUPE_PROPERTY = 'rim_no';

const hubspot = require('@hubspot/api-client');

exports.main = (event, callback) => {
 // Make sure to add your API key under "Secrets" above.
 const hubspotClient = new hubspot.Client({
 accessToken: process.env.[INSERT ACCESS TOKEN]
 });

 hubspotClient.crm.contacts.basicApi
 .getById(event.object.objectId, [DEDUPE_PROPERTY])
 .then(contactResult => {
 let dedupePropValue = contactResult.body.properties[DEDUPE_PROPERTY];

 console.log(`Looking for duplicates based on ${DEDUPE_PROPERTY} = ${dedupePropValue}`);
 hubspotClient.crm.contacts.searchApi
 .doSearch({
 filterGroups: [{
 filters: [{
 propertyName: DEDUPE_PROPERTY,
 operator: 'EQ',
 value: dedupePropValue
 }]
 }]
 })
 .then(searchResults => {
 let idsToMerge = searchResults.body.results
 .map(object => object.id)
 .filter(vid => Number(vid) !== Number(event.object.objectId));

 if (idsToMerge.length == 0) {
 console.log('No matching contact, nothing to merge');
 return;
 } else if (idsToMerge.length > 1) {
 console.log(`Found multiple potential contact IDs ${idsToMerge.join(', ')} to merge`);
 throw new Error("Ambiguous merge; more than one matching contact");
 }

 let idToMerge = idsToMerge[0];
 console.log(`Merging enrolled contact id=${event.object.objectId} into contact id=${idToMerge}`);
 hubspotClient
 .apiRequest({
 method: 'POST',
 path: `/contacts/v1/contact/merge-vids/${idToMerge}`,
 body: {
 vidToMerge: event.object.objectId
 }
 })
 .then(mergeResult => {
 console.log('Contacts merged!');
 });
 });
 });
};

Here’s the new error:

2024-07-25T19:04:33.715Z	ERROR	Unhandled Promise Rejection 	{"errorType":"Runtime.UnhandledPromiseRejection","errorMessage":"TypeError: Cannot read properties of undefined (reading 'properties')","reason":{"errorType":"TypeError","errorMessage":"Cannot read properties of undefined (reading 'properties')","stack":["TypeError: Cannot read properties of undefined (reading 'properties')"," at /var/task/file.js:21:48"," at processTicksAndRejections (node:internal/process/task_queues:96:5)"]},"promise":{},"stack":["Runtime.UnhandledPromiseRejection: TypeError: Cannot read properties of undefined (reading 'properties')"," at process.<anonymous> (file:///var/runtime/index.mjs:1276:17)"," at process.emit (node:events:513:28)"," at emit (node:internal/process/promises:140:20)"," at processPromiseRejections (node:internal/process/promises:274:27)"," at processTicksAndRejections (node:internal/process/task_queues:97:32)"]}
Unknown application error occurred
Runtime.Unknown

Memory: 75/128 MB
Runtime: 1438.88 ms

@CSteinbach were you able to resolve the code issue?

We have created a plugin to make deduplication easy in HubSpot. Of the main feature is a workflow action that merges contact/companies.

I thought this might be relevant!

Best regards,

Hi @CSteinbach :waving_hand:

Looks like you’ve just referenced some keys that weren’t present in the HubSpot API responses. Remove “body” from your “contactResult” and “searchResults” object chains. This has worked on my end, so please feel free to give it a try:

/**
 * Searches for another contact with the same value of DEDUPE_PROPERTY.
 * - If no matches are found, nothing happens
 * - If one match is found, the enrolled contact is merged into the matching contact
 * - If more than one match is found, the action fails
 */

const DEDUPE_PROPERTY = 'rim_no';

const hubspot = require('@hubspot/api-client');

exports.main = (event, callback) => {
 // Make sure to add your API key under "Secrets" above.
 const hubspotClient = new hubspot.Client({
 accessToken: process.env.[INSERT ACCESS TOKEN]
 });

 hubspotClient.crm.contacts.basicApi
 .getById(event.object.objectId, [DEDUPE_PROPERTY])
 .then(contactResult => {
 let dedupePropValue = contactResult.properties[DEDUPE_PROPERTY];

 console.log(`Looking for duplicates based on ${DEDUPE_PROPERTY} = ${dedupePropValue}`);
 hubspotClient.crm.contacts.searchApi
 .doSearch({
 filterGroups: [{
 filters: [{
 propertyName: DEDUPE_PROPERTY,
 operator: 'EQ',
 value: dedupePropValue
 }]
 }]
 })
 .then(searchResults => {
 let idsToMerge = searchResults.results
 .map(object => object.id)
 .filter(vid => Number(vid) !== Number(event.object.objectId));

 if (idsToMerge.length == 0) {
 console.log('No matching contact, nothing to merge');
 return;
 } else if (idsToMerge.length > 1) {
 console.log(`Found multiple potential contact IDs ${idsToMerge.join(', ')} to merge`);
 throw new Error("Ambiguous merge; more than one matching contact");
 }

 let idToMerge = idsToMerge[0];
 console.log(`Merging enrolled contact id=${event.object.objectId} into contact id=${idToMerge}`);
 hubspotClient
 .apiRequest({
 method: 'POST',
 path: `/contacts/v1/contact/merge-vids/${idToMerge}`,
 body: {
 vidToMerge: event.object.objectId
 }
 })
 .then(mergeResult => {
 console.log('Contacts merged!');
 });
 });
 });
};

Finally, if you’re ever looking for an app to handle the merging of Contacts via workflows, you might like to consider Utilities for HubSpot.

I hope this proves helpful. Please let me know if you have any follow-up questions :slightly_smiling_face:

@zach_threadint Thank you so much! I had tried to remove “body” but must have missed something! This works and I really really apprecate your help!!