Custom code workflow fails to find object associations (API call works externally)

Hello everyone,

I’m facing a frustrating issue with a custom code workflow and would appreciate any insights you might have. My workflow’s goal is to associate companies with a ticket by first finding a custom object (“machine”) associated with that ticket.

The core problem is that the code fails to find the associations between the ticket and the custom object when running inside the workflow. However, after extensive troubleshooting, I can confirm that the exact same API call works perfectly fine when executed in an external tool like VS Code.
Here is the code I am using in the custom code action:

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

exports.main = async (event) => {
 const accessToken = process.env.workflow_api;
 const client = new hubspot.Client({ accessToken });

 const ticketId = event.inputFields?.['hs_object_id'];
 if (!ticketId) {
 console.error('Ticket ID is missing.');
 return;
 }

 const MACHINE_OT = '2-33346786';
 const COMPANY_OT = 'company';
 const seen = new Set();

 try {
 // Fetch the ticket and include associations to the "Machine" object
 const ticketWithAssociations = await client.apiRequest({
 method: 'GET',
 path: `/crm/v3/objects/tickets/${ticketId}`,
 qs: {
 associations: MACHINE_OT
 }
 });

 // Extract the Machine IDs from the associations
 const machineAssociations = ticketWithAssociations.associations?.[MACHINE_OT]?.results ?? [];
 const machineIds = machineAssociations.map(assoc => assoc.id);

 if (!machineIds.length) {
 console.warn(`Ticket ${ticketId} has no associated machines.`);
 return;
 }

 console.info(`Found ${machineIds.length} associated machines.`);

 // For each machine, fetch associated companies
 for (const machineId of machineIds) {
 const companyAssociationsResp = await client.apiRequest({
 method: 'GET',
 path: `/crm/v4/objects/${MACHINE_OT}/${machineId}/associations/${COMPANY_OT}`
 });

 const companyIds = companyAssociationsResp.results?.map(r => r.toObjectId) ?? [];

 if (!companyIds.length) {
 console.warn(`Machine ${machineId} has no associated companies.`);
 continue;
 }

 // Associate each company with the ticket
 for (const companyId of companyIds) {
 const key = `${ticketId}:${companyId}`;
 if (seen.has(key)) continue;

 try {
 await client.apiRequest({
 method: 'PUT',
 path: `/crm/v3/objects/companies/${companyId}/associations/tickets/${ticketId}`
 });

 console.info(`Company ${companyId} associated with ticket ${ticketId}.`);
 seen.add(key);
 } catch (assocErr) {
 console.error(`Error associating company ${companyId} with ticket ${ticketId}:`, assocErr.response?.body || assocErr.message);
 }
 }
 }
 } catch (err) {
 console.error('Error in associations workflow:', err.response?.body || err.message || err);
 }
};

When this code runs in the workflow, the ticketWithAssociations.associations object is always empty, resulting in the following log entry:

“Ticket 27316397589 has no associated machines.”
However, when I run the exact API call (GET /crm/v3/objects/tickets/27316397589?associations=2-33346786) in an external tool with a valid Access Token, the API returns the associations correctly.

Could anyone shed some light on why this code would fail to find the associations within the custom code workflow environment? Any advice on potential environment differences or similar issues would be greatly appreciated.

Thank you!

It seems like you need to add some more code for your async/awaits so that it can finish processing the request before going to the next step. It’s likely moving to the next step without the data being available.

Following the sample code here to add a second await for the conversion of the response to json format to ensure that data is available for the rest of the workflow.

https://github.com/HubSpot/sample-workflow-custom-code/blob/c643a39fb9dc8bccfd8a4bd2c8fb38b98b34763d/samples/check_email_and_activity_direction.js#L52

 // Fetch the ticket and include associations to the "Machine" object
 const ticketWithAssociationsRequest = await client.apiRequest({
 method: 'GET',
 path: `/crm/v3/objects/tickets/${ticketId}`,
 qs: {
 associations: MACHINE_OT
 }
 });
 const ticketWithAssociations = await ticketWithAssociationsRequest.json();

Which should let you access the data properly (which you would need to do the same for the other api requests).

Your MACHINE_OT would need to be the fully qualified object name which would be something like “p1234567890_machines” (p portalId underscore object slug). You should see the same info when you did the API request directly.

To clarify about the MACHINE_OT part of my response. You can use a variety of terms for the API request (object id, object type id, qualified name or fully qualified name). But since you’re using the same variable in your check/map of the response data, you would need to access it by what the API returns which is the fully qualified name (for custom objects).