HubSpot Workflow Branch Ignoring Custom Code Output

Hi all,
We’re trying to create a scheduled workflow in HubSpot using Operations Hub Pro that checks if a minimum number of marketing emails have been sent for three specific automated campaigns. If one or more fall below the threshold, we want to:

  • Trigger an internal email notification
  • Use a branch that checks the outcome of a custom code action (alert)

We had the following Workflow setup

  • Workflow trigger: On a scheduled basis (daily)

  • Custom code action:

    • Fetches send stats for 3 marketing emails

    • Compares each to a predefined minimum

    • Returns two outputs:

      • alert → “true” or “false” (String)
      • message → description of the underperforming emails (String)
  • If/then branch:

    • Set to: “Custom code > alert > is equal to > true”

This is the JavaScript code:

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

exports.main = async () => {
 const hubspotClient = new hubspot.Client({ accessToken: process.env.ACCESS_TOKEN });

 const today = new Date();
 today.setUTCHours(0, 0, 0, 0);
 const isoToday = today.toISOString();

 const emailConfigs = [
 { id: '75326555636', name: 'ROPO_Google-mail', min: 110 },
 { id: '61436256971', name: 'ROPO_Kieskeurig_mail', min: 130 },
 { id: '61438281410', name: 'ROPO_Feedbackcompany_mail', min: 240 }
 ];

 const alerts = [];

 try {
 for (const email of emailConfigs) {
 let offset = 0;
 let totalSent = 0;
 let continueFetching = true;

 while (continueFetching) {
 const response = await hubspotClient.apiRequest({
 method: 'GET',
 path: `/marketing-emails/v1/emails/${email.id}/statistics/sends`,
 qs: {
 start: isoToday,
 limit: 100,
 offset: offset
 }
 });

 const sends = response.body?.results ?? [];
 totalSent += sends.length;

 if (sends.length < 100) {
 continueFetching = false;
 } else {
 offset += 100;
 }
 }

 if (totalSent < email.min) {
 alerts.push(`⚠️ ${email.name}: only ${totalSent} sent (min. ${email.min})`);
 }
 }

 const alertStatus = alerts.length > 0 ? 'true' : 'false';
 const messageText = alerts.join('\n') || 'All emails above threshold.';

 console.log('🚦 Alert status:', alertStatus);
 console.log('📨 Message:', messageText);

 return {
 outputFields: {
 alert: alertStatus,
 message: messageText
 }
 };
 } catch (error) {
 console.log('❌ Catch-all error:', error.message);
 return {
 outputFields: {
 alert: 'true',
 message: `❌ Workflow error: ${error.message}`
 }
 };
 }
};

Even though the custom code runs successfully and logs the correct outputs, the branch action consistently fails with:

Action failed: Creates branches based on the missing value outcome of Custom code

It appears that the alert value is not being recognized by the branch, even though:

  • It is defined in the code output
  • It is correctly declared in the “Data outputs” section (as String)
  • It logs correctly in test runs

We’ve tried:

  • Using alert as Boolean, then switching to String
  • Deleting and recreating the branch
  • Re-adding output fields and saving the workflow
  • Logging the output values (they appear as expected)

How can we ensure the custom code output alert is recognized and usable in the branch action? Is there a specific format, type, or known issue with how HubSpot interprets outputs from custom code in branching?
Really looking forward to your ideas.
Kind regard,
Richard

You have to use the callback function in order make it available as an output field. return function doesn’t work.

 callback({
 outputFields: {
 alert: alertStatus,
 message: messageText
 }
 });

If you create a new Custom Coded Action, you can see that the default data from Hubspot uses callback not return.

Hi @RichardRetail25 , you’re very close. The confusing part is that the test run logs can look correct even when the workflow doesn’t actually receive outputs.

In HubSpot custom code actions (Node.js), workflow outputs are reliably passed back via the callback() function, not by returning an object. If you return { outputFields: … }, the code can still execute and print logs, but HubSpot may treat the output as “missing,” which is exactly why the branch says it’s creating branches based on the missing value outcome. HubSpot documents the expected pattern here (Workflows | Custom Code Actions - HubSpot docs )

So the fix is to change your signature and end the action like this (and I’d strongly recommend making alert a real boolean, not the string “true”): exports.main = async (event, callback) => { …; callback({ outputFields: { alert: alerts.length > 0, message: messageText } }); }

Then define the output type in the action as Boolean and branch on “is equal to true.” That removes a whole class of “string vs boolean” weirdness in if/then branches.

One more small tip: keep your catch block using the same callback pattern too, otherwise errors will also show as “missing outputs” instead of taking your true/false path.

That should make the branch behave consistently.

Hi Rubin,
Thank you for your feedback. I’ve put it through and are now testing it. I’ll let you know if it works because the previous solutions didn’t work.
Thanks