• Live group demo of Marketing Hub + Data Agent

    Standardize reporting, reduce manual work, and introduce AI without cleanup

    Join us on March 12
  • Ready to build your local HubSpot community?

    HUG leaders host events, spark connections, and create spaces where people learn and grow together.

    Become a HUG Leader

Custom code and Company deduplication

GJakobTriad
Participant

Hey guys. I'm looking for some help with this. I'm trying to unlock the potential of the custom code element present in workflows, the one granted by Operations Hub.

 

I'm basically trying to adapt the following sample code to work with Companies, and use their names as the deduplication property:

https://github.com/HubSpot/sample-workflow-custom-code/blob/main/samples/dedupe_contact.js

 

But I get the following, basic-looking error pointing at line 20, which is the first time the code tries to search by ID. For some reason, it fails.

ERROR Invoke Error {"errorType":"TypeError","errorMessage":"Cannot read properties of undefined (reading 'getById')","stack":["TypeError: Cannot read properties of undefined (reading 'getById')"," at Object.exports.main (/var/task/file.js:20:6)"," at Runtime.exports.hubspot_handler [as handler] (/var/task/hubspotHandler.js:6:21)"," at Runtime.handleOnceNonStreaming (file:///var/runtime/index.mjs:1173:29)"]}

 

Here's the full code. I tried both referencing the ID using the sample's "event.object.objectId" and also feeding it as an input field on the workflow. The error remains the same.

 

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

const DEDUPE_PROPERTY = 'name';

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.CompanyAssocCreateToken
  });

  hubspotClient.crm.companies.defaultApi
    .getById(event.object.objectId, [DEDUPE_PROPERTY])
    .then(companyResult => {
      let dedupePropValue = companyResult.body.properties[DEDUPE_PROPERTY];

      console.log(`Looking for duplicates based on ${DEDUPE_PROPERTY} = ${dedupePropValue}`);
      hubspotClient.crm.companies.defaultApi
        .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 company, nothing to merge');
            return;
          } else if (idsToMerge.length > 1) {
            console.log(`Found multiple potential company IDs ${idsToMerge.join(', ')} to merge`);
            throw new Error("Ambiguous merge; more than one matching company");
          }

          let idToMerge = idsToMerge[0];
          console.log(`Merging enrolled company id=${event.object.objectId} into company id=${idToMerge}`);
          hubspotClient
            .apiRequest({
              method: 'POST',
              path: `/crm/v3/objects/companies/merge`,
              body: {
                objectIdToMerge: idToMerge,
                primaryObjectId: event.object.objectId
              }
            })
            .then(mergeResult => {
              console.log('Companies merged!');
            });
        });
    });

    callback({
      outputFields: {
        company_id: event.object.objectId
      }
    });

};

 

2 Accepted solutions
GJakobTriad
Solution
Participant

After a bunch of troubleshooting, I found the two issues which were breaking the code:

 

  1. I was calling the wrong APIs, which should be basicApi and searchApi instead of DefaultApi. The Company Endpoints documentation was misleading in this regard.

  2. The sample code refers to "body.properties" when trying to look into the properties clause of a response, and "body" throws it off, so that's inaccurate too. The reference should only be "properties", as in companyResult.properties[DEDUPE_PROPERTY].

View solution in original post

romeonoi
Solution
Participant | Elite Partner
Participant | Elite Partner

Based on all this, I did this repo: https://github.com/romeoman/hubspot-company-dedup

Enhanced it a bit with some tracking.

hope it helps.

View solution in original post

0 Upvotes
7 Replies 7
romeonoi
Solution
Participant | Elite Partner
Participant | Elite Partner

Based on all this, I did this repo: https://github.com/romeoman/hubspot-company-dedup

Enhanced it a bit with some tracking.

hope it helps.

0 Upvotes
Jaycee_Lewis
Thought Leader

Thanks for sharing @romeonoi 🙌 — Jaycee





loop


Loop Marketing is a new four-stage approach that combines AI efficiency and human authenticity to drive growth.

Learn More




0 Upvotes
BreannahGladden
Participant

hey - I was SO HAPPY when I saw this; I've attempted to use this on my own for a client and get an error. Any advice? Am I missing something? 😥

WARNING: The logs for this function have exceeded the 4KB limit.
...
2024-01-25T21:08:06.071Z	undefined	ERROR	Uncaught Exception 	{"errorType":"Runtime.UserCodeSyntaxError","errorMessage":"SyntaxError: Unexpected token '['","stack":["Runtime.UserCodeSyntaxError: SyntaxError: Unexpected token '['","    at _loadUserApp (file:///var/runtime/index.mjs:1084:17)","    at async Object.UserFunction.js.module.exports.load (file:///var/runtime/index.mjs:1119:21)","    at async start (file:///var/runtime/index.mjs:1282:23)","    at async file:///var/runtime/index.mjs:1288:1"]}
INIT_REPORT Init Duration: 172.64 ms	Phase: init	Status: error	Error Type: Runtime.ExitError
2024-01-25T21:08:07.463Z	undefined	ERROR	Uncaught Exception 	{"errorType":"Runtime.UserCodeSyntaxError","errorMessage":"SyntaxError: Unexpected token '['","stack":["Runtime.UserCodeSyntaxError: SyntaxError: Unexpected token '['","    at _loadUserApp (file:///var/runtime/index.mjs:1084:17)","    at async Object.UserFunction.js.module.exports.load (file:///var/runtime/index.mjs:1119:21)","    at async start (file:///var/runtime/index.mjs:1282:23)","    at async file:///var/runtime/index.mjs:1288:1"]}
INIT_REPORT Init Duration: 1622.43 ms	Phase: invoke	Status: error	Error Type: Runtime.ExitError
START RequestId: ee8cb700-9ee8-411a-a750-0d62c8409311 Version: $LATEST
Unknown application error occurred
Runtime.UserCodeSyntaxErroran 

 

0 Upvotes
Clare_HS
Participant

Hi, @GJakobTriad... thanks so much for posting your solution - it was enormously helpful!

I also used the contact dedupe snippet from GitHub and combined that with your revisions. The code executed successfully but wasn't doing anything (the companies weren't merging). 

I found that I had a discrepancy further down in my combined code, so I'm reposting the full, working code block here for other folks who may be runing into the same issue.

 

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

const DEDUPE_PROPERTY = 'domain';

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.companies.basicApi
    .getById(event.object.objectId, [DEDUPE_PROPERTY])
    .then(companyResult => {
      let dedupePropValue = companyResult.properties[DEDUPE_PROPERTY];

      console.log(`Looking for duplicates based on ${DEDUPE_PROPERTY} = ${dedupePropValue}`);
      hubspotClient.crm.companies.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 company, nothing to merge');
            return;
          } else if (idsToMerge.length > 1) {
            console.log(`Found multiple potential company IDs ${idsToMerge.join(', ')} to merge`);
            throw new Error("Ambiguous merge; more than one matching company");
          }

          let idToMerge = idsToMerge[0];
          console.log(`Merging enrolled company id=${event.object.objectId} into company id=${idToMerge}`);
          hubspotClient
            .apiRequest({
              method: 'POST',
              path: `/crm/v3/objects/companies/merge`,
              body: {
                objectIdToMerge: idToMerge,
                primaryObjectId: event.object.objectId
              }
            })
            .then(mergeResult => {
              console.log('Companies merged!');
            });
        });
    });

    callback({
      outputFields: {
        company_id: event.object.objectId
      }
    });
};

 

0 Upvotes
GJakobTriad
Solution
Participant

After a bunch of troubleshooting, I found the two issues which were breaking the code:

 

  1. I was calling the wrong APIs, which should be basicApi and searchApi instead of DefaultApi. The Company Endpoints documentation was misleading in this regard.

  2. The sample code refers to "body.properties" when trying to look into the properties clause of a response, and "body" throws it off, so that's inaccurate too. The reference should only be "properties", as in companyResult.properties[DEDUPE_PROPERTY].
MiaSrebrnjak
Community Manager
Community Manager

@GJakobTriad you're a , kudos to you!

Thank you for sharing the solution with the Community 💛 

 


loop Loop Marketing is a new four-stage approach that combines AI efficiency and human authenticity to drive growth.
Learn More

0 Upvotes
MiaSrebrnjak
Community Manager
Community Manager

Hi @GJakobTriad,

 

Thank you for reaching out to the Community! 

I wanted to tag in a couple of subject matter experts to see if they have any input on this matter:

Hi @Teun@ChristinaKay@DanielJeal, do you have any tips for @GJakobTriad? Thank you!      

 

Cheers
Mia, Community Team


loop Loop Marketing is a new four-stage approach that combines AI efficiency and human authenticity to drive growth.
Learn More

0 Upvotes