Custom Code in Deal Workflow to create ticket

Hello,

If someone can help me identify why my custom code is not working for a Deal Workflow to Create a Ticket and bring Ticket Properties I need.

We are building a custom HubSpot workflow that:

  • Triggers when a quote is marked “Ready for Booking” and hasn’t been signed yet.
  • Creates a ticket to initiate order entry.
  • Automatically populates ticket fields with key deal data:
    • Earliest start date from the quote’s line items

    • Company’s internal NavigaID (custom company property)

      But it the fields are coming back empty on the ticket.

      This is what is my logic:

      • Take the deal ID (hs_object_id)

      • Fetch all quotes associated with the deal

      • Sort them to find the most recent quote

      • Retrieve the line items associated with that quote

      • Identify the earliest start date

      • Return it for use in the workflow

          I have a secret token that has these scopes:
        
        • crm.objects.deals.read

        • crm.objects.quotes.read

        • crm.objects.line_items.read

        • crm.schemas.line_items.read

        • crm.objects.companies.read

        • crm.associations.read

                  <strong>**Here is the code I am using:** </strong>
          
                  const hubspot = require('@hubspot/api-client');
          
                  exports.main = async (event, callback) => {
                  const hs = new hubspot.Client({ accessToken: process.env.Line\_Item\_API });
                  const dealId = event.inputFields\['hs\_object\_id'];
                  let debugLog = "Step 1: Start\n";
          
                  try {
                  // Step 2: Get quotes associated with the deal
                  debugLog += "Step 2: Fetching associated quotes...\n";
                  const quoteResults = await hs.crm.deals.associationsApi.getAll(dealId, 'quotes');
                  const quotes = quoteResults.results \|\| \[];
          
                  debugLog += \`Quotes found: ${quotes.length}\n\`;
          
                  if (quotes.length === 0) {
                  debugLog += "No quotes found for deal.\n";
                  callback({
                  outputFields: {
                  earliest\_start\_date: null,
                  debug\_log: debugLog
                  }
                  });
                  return;
                  }
          
                  // Step 3: Use most recent quote
                  const latestQuoteId = quotes\[0].id;
                  debugLog += \`Using quote ID: ${latestQuoteId}\n\`;
          
                  // Step 4: Get line items for the quote
                  const lineItemResults = await hs.crm.quotes.associationsApi.getAll(latestQuoteId, 'line\_items');
                  const lineItems = lineItemResults.results \|\| \[];
          
                  debugLog += \`Line items found: ${lineItems.length}\n\`;
          
                  if (lineItems.length === 0) {
                  debugLog += "No line items found for quote.\n";
                  callback({
                  outputFields: {
                  earliest\_start\_date: null,
                  debug\_log: debugLog
                  }
                  });
                  return;
                  }
          
                  // Step 5: Fetch full details of line items to access start\_date
                  const fullLineItems = await Promise.all(
                  lineItems.map(item => hs.crm.lineItems.basicApi.getById(item.id))
                  );
          
                  const startDates = fullLineItems
                  .map(res => res.body.properties?.start\_date)
                  .filter(date => !!date)
                  .sort();
          
                  if (startDates.length === 0) {
                  debugLog += "No valid start dates found.\n";
                  callback({
                  outputFields: {
                  earliest\_start\_date: null,
                  debug\_log: debugLog
                  }
                  });
                  return;
                  }
          
                  const earliest = startDates\[0];
                  debugLog += \`Earliest start date: ${earliest}\n\`;
          
                  callback({
                  outputFields: {
                  earliest\_start\_date: earliest,
                  debug\_log: debugLog
                  }
                  });
          
                  } catch (error) {
                  debugLog += \`Error: ${error.message \|\| error.toString()}\n\`;
                  callback({
                  outputFields: {
                  earliest\_start\_date: null,
                  debug\_log: debugLog
                  }
                  });
                  }
                  };
          

Hi @SRincon8,

Thanks for reaching out to the Community!

I would like to invite some members of our community who may offer valuable insights.— hey @nickdeckerdevs1, @WesQ, @skimura - Could you share your advice with @SRincon8?

Thanks for taking a look!

Diana

Hi there! @SRincon8,

Can you give us more details?

1) What error message are you getting?

2) At what part of the logic are you having problems?

  • Take the deal ID (hs_object_id)
  • Fetch all quotes associated with the deal
  • Sort them to find the most recent quote
  • Retrieve the line items associated with that quote
  • Identify the earliest start date
  • Return it for use in the workflow

Excatly -- we have to understand what is going wrong here before we really determine the issue or we are wild goose chasing.. I like wild goose chasing on a Friday, never a Monday.

Sorry about that.

I am not gettting any errors. The deal enrolls in the workflow and created the ticket, however it does not bring any infomration on the Deal Property Field: Earliest Line Item Start Date.

i have another Property Deal Created Debug text to tell me what is happening and this is the result: Step 1: Start
Quotes found: Step 1: Start
Quotes found: 0

Yet, there is a quote in Piublished status and the line items from the Quote are showing in the line items in the deal.

These are the event details of the code:

eturn value

[{“fieldKey”:“earliest_start_date”,“value”:“”},{“fieldKey”:“company_navigaid”,“value”:“”},{“fieldKey”:“debug_text”,“value”:“Step 1: Start\nQuotes found: 0”}]

Yes, the company_navigaid is showing up Blank too in the Deal Property.

Does this help?

Sorry about that.

I am not gettting any errors. The deal enrolls in the workflow and created the ticket, however it does not bring any infomration on the Deal Property Field: Earliest Line Item Start Date.

i have another Property Deal Created Debug text to tell me what is happening and this is the result: Step 1: Start
Quotes found: Step 1: Start
Quotes found: 0

Yet, there is a quote in Piublished status and the line items from the Quote are showing in the line items in the deal.

These are the event details of the code:

eturn value

[{“fieldKey”:“earliest_start_date”,“value”:“”},{“fieldKey”:“company_navigaid”,“value”:“”},{“fieldKey”:“debug_text”,“value”:“Step 1: Start\nQuotes found: 0”}]

Yes, the company_navigaid is showing up Blank too in the Deal Property.

Does this help?

Here is the code again, I did some updated, but still experiencing the same results:

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

exports.main = async (event, callback) => {
const client = new hubspot.Client({ accessToken: process.env.Line_Item_API });
const dealId = event.inputFields[‘hs_object_id’];
let debug = [];

try {
debug.push(“Step 1: Start”);

// STEP 2: Get quotes associated with the deal
const quoteAssoc = await client.apiRequest({
method: ‘GET’,
path: `/crm/v4/objects/deals/${dealId}/associations/quotes`
});

const quoteIds = quoteAssoc.body?.results?.map(r => r.toObjectId) || [];
debug.push(`Quotes found: ${quoteIds.length}`);

if (quoteIds.length === 0) {
callback({ outputFields: { earliest_start_date: ‘’, company_navigaid: ‘’, debug_text: debug.join(‘\n’) } });
return;
}

// STEP 3: Pick the most recent quote based on createdate
const quoteBatch = await client.crm.quotes.batchApi.read({
inputs: quoteIds.map(id => ({ id })),
properties: [‘createdate’]
});

const sortedQuotes = quoteBatch.results
.map(q => ({
id: q.id,
createdAt: new Date(q.properties.createdate)
}))
.sort((a, b) => b.createdAt - a.createdAt);

const latestQuoteId = sortedQuotes[0].id;
debug.push(`Latest quote ID: ${latestQuoteId}`);

// STEP 4: Get line items from the quote
const lineItemAssoc = await client.apiRequest({
method: ‘GET’,
path: `/crm/v4/objects/quotes/${latestQuoteId}/associations/line_items`
});

const lineItemIds = lineItemAssoc.body?.results?.map(r => r.toObjectId) || [];
debug.push(`Line items found: ${lineItemIds.length}`);

if (lineItemIds.length === 0) {
callback({ outputFields: { earliest_start_date: ‘’, company_navigaid: ‘’, debug_text: debug.join(‘\n’) } });
return;
}

// STEP 5: Get each line item and extract Start_Date
const lineItems = await client.crm.lineItems.batchApi.read({
inputs: lineItemIds.map(id => ({ id })),
properties: [‘Start_Date’]
});

const startDates = lineItems.results
.map(item => item.properties?.Start_Date)
.filter(Boolean)
.map(date => new Date(date));

if (startDates.length === 0) {
debug.push(“No valid Start_Date values found.”);
callback({ outputFields: { earliest_start_date: ‘’, company_navigaid: ‘’, debug_text: debug.join(‘\n’) } });
return;
}

const earliest = new Date(Math.min(…startDates)).toISOString().split(‘T’)[0];
debug.push(`Earliest start date: ${earliest}`);

// STEP 6: Get associated company and NavigaID
const companyAssoc = await client.apiRequest({
method: ‘GET’,
path: `/crm/v4/objects/deals/${dealId}/associations/companies`
});

const companyIds = companyAssoc.body?.results?.map(r => r.toObjectId) || [];

let navigaid = ‘’;
if (companyIds.length > 0) {
const company = await client.crm.companies.basicApi.getById(companyIds[0], [‘navigaid’]);
navigaid = company.properties.navigaid || ‘’;
debug.push(`NavigaID found: ${navigaid}`);
} else {
debug.push(‘No company associated.’);
}

// STEP 7: Final output
callback({
outputFields: {
earliest_start_date: earliest,
company_navigaid: navigaid,
debug_text: debug.join(‘\n’)
}
});

} catch (err) {
debug.push(`ERROR: ${err.message}`);
callback({
outputFields: {
earliest_start_date: ‘’,
company_navigaid: ‘’,
debug_text: debug.join(‘\n’)
}
});
}
};

On mobile so difficult to debug.
to confirm earliest time a not being output, that is the problem?

can you just declare earliest at the start of your workflow main function and that resolves this? Or set earliest in the output ?

It is the earliest Start Date of an line item. A quote may have more than 1 line item and each has their own Start Date. I want to get the Start Date of the line items thats has the earliest Start Date of all the line items.

Well, first lets figure out what is being returned from this batch call

const lineItems = await client.crm.lineItems.batchApi.read({
 inputs: lineItemIds.map(id => ({ id })),
 properties: ['Start_Date']
});

debug.push(`Line Items Found: ${JSON.stringify(lineItems.results)}`);

const startDates = lineItems.results
 .map(item => item.properties?.Start_Date)
 .filter(Boolean)
 .map(date => new Date(date));

I don’t use the hubspot sdk, but all properties are stored as lowercase inside HubSpot, not sure if the sdk translates that -- but we also push this information so we can make sure we have actual data going into the map/filter/map
I’m thinking the code may be more like this:

const lineItems = await client.crm.lineItems.batchApi.read({
 inputs: lineItemIds.map(id => ({ id })),
 properties: ['start_date']
});

debug.push(`Line Items Found: ${JSON.stringify(lineItems.results)}`);

const startDates = lineItems.results
 .map(item => item.properties?.start_date)
 .filter(Boolean)
 .map(date => new Date(date));

I added more debug and now I get this error:
Deal ID: 36858518450 :cross_mark: Error: hubspotClient.crm.associations.v4.basicApi.getAll is not a function Stack: TypeError: hubspotClient.crm.associations.v4.basicApi.getAll is not a function at exports.main (/var/task/file.js:11:80) at exports.hubspot_handler [as handler] (/var/task/hubspotHandler.js:7:21) at Runtime.handleOnceNonStreaming (file:///var/runtime/index.mjs:1173:29)

I got earlier error with: associatiionsAPI
Deal ID: 36858514850 :cross_mark: Error: Cannot read properties of undefined (reading ‘getAll’) Stack: TypeError: Cannot read properties of undefined (reading ‘getAll’) at exports.main (/var/task/file.js:12:77) at exports.hubspot_handler [as handler] (/var/task/hubspotHandler.js:7:21) at Runtime.handleOnceNonStreaming (file:///var/runtime/index.mjs:1173:29)

I can’t get passed this.

Did you add what I suggested or did you change other code?
I don’t see “hubspotClient.crm.associations.v4.basicApi.getAll” in any of the code you have supplied, that is likely not a proper way to pull in associations, if it is saying “is not a function”
Can you use the code below, and then paste out what is in the debug that gets output if there are errors?

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

exports.main = async (event, callback) => {
 const client = new hubspot.Client({ accessToken: process.env.Line_Item_API });
 const dealId = event.inputFields['hs_object_id'];
 let debug = [];

 try {
 debug.push("Step 1: Start");

 // STEP 2: Get quotes associated with the deal
 const quoteAssoc = await client.apiRequest({
 method: 'GET',
 path: `/crm/v4/objects/deals/${dealId}/associations/quotes`
 });

 const quoteIds = quoteAssoc.body?.results?.map(r => r.toObjectId) || [];
 debug.push(`Quotes found: ${quoteIds.length}`);

 if (quoteIds.length === 0) {
 callback({ outputFields: { earliest_start_date: '', company_navigaid: '', debug_text: debug.join('\n') } });
 return;
 }

 // STEP 3: Pick the most recent quote based on createdate
 const quoteBatch = await client.crm.quotes.batchApi.read({
 inputs: quoteIds.map(id => ({ id })),
 properties: ['createdate']
 });

 const sortedQuotes = quoteBatch.results
 .map(q => ({
 id: q.id,
 createdAt: new Date(q.properties.createdate)
 }))
 .sort((a, b) => b.createdAt - a.createdAt);

 const latestQuoteId = sortedQuotes[0].id;
 debug.push(`Latest quote ID: ${latestQuoteId}`);

 // STEP 4: Get line items from the quote
 const lineItemAssoc = await client.apiRequest({
 method: 'GET',
 path: `/crm/v4/objects/quotes/${latestQuoteId}/associations/line_items`
 });

 const lineItemIds = lineItemAssoc.body?.results?.map(r => r.toObjectId) || [];
 debug.push(`Line items found: ${lineItemIds.length}`);

 if (lineItemIds.length === 0) {
 callback({ outputFields: { earliest_start_date: '', company_navigaid: '', debug_text: debug.join('\n') } });
 return;
 }

 // STEP 5: Get each line item and extract Start_Date
 const lineItems = await client.crm.lineItems.batchApi.read({
 inputs: lineItemIds.map(id => ({ id })),
 properties: ['start_date']
 });

 debug.push(`Line Items Found: ${JSON.stringify(lineItems)}`);

 const startDates = lineItems.results
 .map(item => item.properties?.start_date)
 .filter(Boolean)
 .map(date => new Date(date));

 if (startDates.length === 0) {
 debug.push("No valid Start_Date values found.");
 callback({ outputFields: { earliest_start_date: '', company_navigaid: '', debug_text: debug.join('\n') } });
 return;
 }

 const earliest = new Date(Math.min(...startDates)).toISOString().split('T')[0];
 debug.push(`Earliest start date: ${earliest}`);

 // STEP 6: Get associated company and NavigaID
 const companyAssoc = await client.apiRequest({
 method: 'GET',
 path: `/crm/v4/objects/deals/${dealId}/associations/companies`
 });

 const companyIds = companyAssoc.body?.results?.map(r => r.toObjectId) || [];

 let navigaid = '';
 if (companyIds.length > 0) {
 const company = await client.crm.companies.basicApi.getById(companyIds[0], ['navigaid']);
 navigaid = company.properties.navigaid || '';
 debug.push(`NavigaID found: ${navigaid}`);
 } else {
 debug.push('No company associated.');
 }

 // STEP 7: Final output
 callback({
 outputFields: {
 earliest_start_date: earliest,
 company_navigaid: navigaid,
 debug_text: debug.join('\n')
 }
 });

 } catch (err) {
 debug.push(`ERROR: ${err.message}`);
 callback({
 outputFields: {
 earliest_start_date: '',
 company_navigaid: '',
 debug_text: debug.join('\n')
 }
 });
 }
};

This is what i get now

So this is the part that isn’t finding assocated quotes. I don’t see any v4 -- Are you using AI to generate this? AI is super unreliable -- I would manually test out these api calls inside something like postman or locally from your machine before putting them into a workflow. there is no v4 for deals api
https://api.hubapi.com/crm/v3/objects/deals/{dealId}

const quoteAssoc = await client.apiRequest({
 method: 'GET',
 path: `/crm/v4/objects/deals/${dealId}/associations/quotes`
 });

I was abel to resolve it. Thank you all for your feedback here. I appreciate it. I ended up using getPage and that did it.