Associating objects using HubSpot's custom-coded workflows


HubSpot's Operations Hub Professional/Enterprise includes the option to create custom-coded workflow actions. This dramatically expands the capabilities of the workflows tool and means you don't have to resort to middleware tools such as Zapier or Make, and even eliminates most times when you need to create custom-code in a tool such as Pipedream, Google Cloud or AWS.

One area where custom-coded workflows makes up for a frustrating lack of functionality in the workflows tool is in automatically associating objects together. In this blog post, we'll show you how to do that for a deal record with an associated company and custom object. We'll include an explanation of each step of the code or, if you prefer, then you can simply copy and paste our entire code.

Goal and acceptance criteria

The goal of this workflow is to:

  • find the company record associated with a deal
  • find the custom object record associated with a deal
  • associate those objects with each other.

The acceptance criteria for the workflow is:

  • Given: a deal exists with an associated company and associated custom object record
  • When: the deal enters a specific deal stage
  • Then: the associated company and associated custom object are associated with each other.

Identifying the deal

For this use case, the workflow needs to be activated based on a specific deal. As a result, we are going to use a deal-based workflow. Note that this means that, in addition to your Operations Hub Professional/Enterprise subscription, you will also need either Sales Hub Professional or Sales Hub Enterprise.

Create your deal-based workflow and set the enrolement criteria as appropriate (probably based on the deal being in a specific stage of your pipeline).

The next action in your workflow should be 'custom code'.

It's important that your custom code knows which deal to perform actions on, so we need to do two things:

  1. Tell the custom-coded action to include the Record ID of the deal as a variable
  2. Include code that saves that variable in the code's scope.

You achieve the first of these steps using the dropdown menu in the custom-code settings:

Declare the name of the variable as you'll reference it in your code in the left-hand field and then choose the deal property in the dropdown field.

Next, you'll need to include code that declares the variable:

const dealId = event.inputFields['dealId'];

Find the associated company

Now we want to find the company associated with the deal. To do this, we'll use the HubSpot Associations API. To do that, we'll first need to configure a couple of variables:

const toObjectType = "COMPANY";
const limit = 1;

The first of these variables (`toObjectType`) tells our code that we're looking for company records associated with the deal. The second (`limit`) ensures that we only receive a single record back from our API call (this code could be easily modified to work with multiple associations but our use case is based on a single company record).

Next, we need to use the API to find the associated company and store its record ID as a variable called `companyId`. We can do that with just eleven lines of code (we could do it with fewer lines of code but the example here includes some additional lines to help with any debugging needs in future):

try {
 const apiResponse = await hubspotClient.crm.deals.associationsApi.getAll(dealId, toObjectType, limit);
 console.log(JSON.stringify(apiResponse.body, null, 2));
 const companyArray = apiResponse.body.results ;
 var companyId = companyArray[0].id ;
 console.log("The companyId is: " + companyId );  
} catch (e) {
 e.message === 'HTTP request failed'
  ? console.error(JSON.stringify(e.response, null, 2))
  : console. Error(e)
}  

Our code is now able to find the Record ID of company objects associated with our deal.

Find the associated custom object

To find the custom object record associated with the deal, we'll use some very similar code. This time, however, our initial variable will look a little different. In the HubSpot API, it's much easier to reference a custom object using a unique numeric identifier rather than its name.

Whereas our company variable looked like this:

const toObjectType = "COMPANY"; 

Our custom object variable will look something like this:

const toCustomType = "2-123456";

You can find the numeric unique ID of your custom object by looking in HubSpot's listing view (click 'Contacts' and then the name of your custom object):

Then look in the URL bar of your browser. The unique identifier can be found after `/objects/` and will begin with `2-`:

With that additional variable declared, we can now add our very slightly modified Associations API code:

 

try {
 const apiCustomResponse = await hubspotClient.crm.deals.associationsApi.getAll(dealId, toCustomType, limit);
 console.log(JSON.stringify(apiCustomResponse.body, null, 2));
 const customArray = apiCentreResponse.body.results ;
 var customId = customArray[0].id ;
 console.log("The custom object record is is: " + customId );  
} catch (e) {
 e.message === 'HTTP request failed'
  ? console.error(JSON.stringify(e.response, null, 2))
  : console. Error(e)
}  

This time, the Record ID of the custom object is stored in a variable called `customId`.

Associating the objects together

Now that we know the Record ID of both the company and the custom object, we can associate them together. To achieve this, we'll use a different endpoint of the Associations API.

Our code will find the company record and then associate the custom object record with it.

To do this, we'll start by declaring a couple of variables again:

 const BatchInputPublicAssociation = { inputs: [{"from":{"id": `${companyId}`},"to":{"id":`${customId}`},"type":"101"}] };
 
const fromObjectType = "COMPANY";

While declaring your `BatchInputPublicAssociation` variable, you'll need to know the association type identifier. You can find this, again, by looking in the URL bar while logged into HubSpot. Go to 'Settings -> Objects -> Companies' and then click the 'Associations' tab. Find the association type that you're trying to use and click 'edit'.

You'll find the ID between `/edit-association-label/` and `?type`. In our case, this identifier is `101`. You'll place this identifier as the `type` value of the `BatchInputPublicAssociation` API.

With that variable now declared, you can now create the association using this code:

 try {
  const apiResponse = await hubspotClient.crm.associations.batchApi.create(fromObjectType, toCustomType, BatchInputPublicAssociation);
  console.log(JSON.stringify(apiResponse.body, null, 2));
 } catch (e) {
  e.message === 'HTTP request failed'
   ? console.error(JSON.stringify(e.response, null, 2))
  : console. Error(e)
}

The complete code

We now have the complete code required for the process:

 

const hubspot = require('@hubspot/api-client');
exports.main = async (event, callback) => {
   
 const dealId = event.inputFields['dealId'];
  
// Find the associated company
const hubspotClient = new hubspot.Client({"accessToken": process.env.PUTYOURSECRETNAMEHERE});

const toObjectType = "COMPANY";
const toCustomType = "2-123456";

const limit = 1;

// Gets the Company ID of the deal in this workflow
try {
 const apiResponse = await hubspotClient.crm.deals.associationsApi.getAll(dealId, toObjectType, limit);

 console.log(JSON.stringify(apiResponse.body, null, 2));
 const companyArray = apiResponse.body.results ;
 var companyId = companyArray[0].id ;
 console.log("The companyId is: " + companyId );  
} catch (e) {
 e.message === 'HTTP request failed'
  ? console.error(JSON.stringify(e.response, null, 2))
  : console.error(e)
}  

// Gets the Custom Record ID of the deal in this workflow  
try {
 const apiCentreResponse = await hubspotClient.crm.deals.associationsApi.getAll(dealId, toCustomType, limit);
 console.log(JSON.stringify(apiCustomResponse.body, null, 2));
 const customArray = apiCustomResponse.body.results ;
 var customId = customArray[0].id ;
 console.log("The custom object Record ID is: " + centreId );  
} catch (e) {
 e.message === 'HTTP request failed'
  ? console.error(JSON.stringify(e.response, null, 2))
  : console.error(e)
}   
  
// Associates the Custom Object and Company together  

 const BatchInputPublicAssociation = { inputs: [{"from":{"id": `${companyId}`},"to":{"id":`${customId}`},"type":"101"}] };

 const fromObjectType = "COMPANY";
  
 try {
  const apiResponse = await hubspotClient.crm.associations.batchApi.create(fromObjectType, toCustomType, BatchInputPublicAssociation);
  console.log(JSON.stringify(apiResponse.body, null, 2));
 } catch (e) {
  e.message === 'HTTP request failed'
   ? console.error(JSON.stringify(e.response, null, 2))
  : console.error(e)
 }
  
 callback({
  outputFields:
  }
 });
}

Note that the code above includes default code that comes when creating a custom-coded action. Some of this isn't strictly necessary but it's handy to keep it in there in case you want to expand the functionality of the workflow in future (for example, returning a variable to the workflow for use in a further step).

Do you need help with custom-coded workflow actions

If all of this sounds intimidating and you want help with your custom-coded actions, let our team at SpotDev know. Our experts are ready and waiting to work with you to write custom-coded workflow actions tailored specifically for your business needs so that you can focus on how you'll use the data.

Our content includes affiliate links. This means that we may receive a commission if you make a purchase through one of the links on our website. This will be at no cost to you and helps to fund the content creation work on our website.