Debugging Custom Code in Hubspot Workflows

Share This Post

If you have previously read our article of how to utilize custom code in HubSpot Workflows you will by now be aware of some of the tools that HubSpot provides to be able to manipulate data, both inside and outside of HubSpot during the processing of a workflow. If you have not read the article linked above, we recommend doing so before proceeding.

To reiterate, using custom code in Workflows will only be available to you if you are a  marketing hub professional, enterprise sales hub professional, enterprise service hub professional or enterprise operations hub professional customer. Please contact your HubSpot representative to discuss your needs and upgrade your account if you wish to make use of these powerful tools.

Let's Begin!

We’ll pick back up where left off with the previous example. In the previous example we were using custom code to unsubscribe a contact in DotDigital whenever a HubSpot contact has opted out of any and all customer communications. Here is the code from our previous example:

				
					const hubspot = require('@hubspot/api-client');
const https = require('follow-redirects').https;

exports.main = (event, callback) => {

    // Secrets can be accessed via environment variables.
    // Make sure to add your HAPIKEY under "Secrets" above.
    const hubspotClient = new hubspot.Client({
        apiKey: process.env.HAPIKEY
    });

    // First, we need to find the contact that's associated with this entity
    let currentContactID = event.object.objectId;

    console.log("******* Current contact ID is: " + currentContactID);

    fetchContactByObjectId(hubspotClient, currentContactID).then(currentContact => {

        let currentContactsEmail = currentContact.properties.email;

        console.log("****** Contact email for current contact: " + currentContactsEmail);

        unsubscribeInDotdigital(currentContactsEmail).then(unsubscribeResponse => {

            callback({
                outputFields: {
                    results: JSON.stringify(unsubscribeResponse)
                }
            });



        });


    });

}

function fetchContactByObjectId(hubspotClient, contactId) {
    console.log("ENTERING: fetchContactByObjectId");

    return new Promise(function (resolve, reject) {
        // Get associated contacts for the current deal
        hubspotClient.crm.contacts.basicApi.getById(contactId)
            .then(results => {
                resolve(results.body);
            })
            .catch(err => {
                console.error("***** Got an exception while attempting to load current contact: " + err.message);
                reject(err);
            });
    });

}


function unsubscribeInDotdigital(emailToUnsubscribe) {
    console.log("ENTERING: unsubscribeInDotdigital");

    return new Promise(function (resolve, reject) {

        let options = {
            'method': 'POST',
            'hostname': 'r2-api.dotmailer.com',
            'path': '/v2/contacts/unsubscribe',
            'headers': {
                'Content-Type': 'application/json',
                'Authorization' : 'Basic ' + process.env.DOTDIGITAL_CREDS_BASE64
            },
            'maxRedirects': 20
        };
        let req = https.request(options, function (res) {
            let chunks = [];
            res.on("data", function (chunk) {
                chunks.push(chunk);
            });
            res.on("end", function (chunk) {
                let body = Buffer.concat(chunks);
                console.log("****** Made API call to merge. Resulting body: " + body.toString());
                resolve(body);
            });
            res.on("error", function (error) {
                console.error(error);
                reject(error);
            });
        });

        let postData = JSON.stringify({
            "email": emailToUnsubscribe
        });
        req.write(postData);
        req.end();

    });

}

				
			

For the purpose of this article, it is less important what this code actually does than HOW it does it.

Custom code in HubSpot is powered by Node.JS, more specifically Node.JS 12. This version cannot be changed. When debugging, it is helpful to know what version you are using as some features or functionality may or may not be available from one version to the other.

When processing your workflow, if HubSpot encounters a “custom code” action, it will process the defined block of code through a serverless function using AWS Lambda.

HubSpot expects your custom code to be formatted using some main structural components.  There are some main structural components:

  • The main()function is called when the code snippet action is executed.
  • The eventargument is an object containing details for the workflow execution.
  • The callback()function is used to pass data back to the workflow. It should be called in the main function.

Within your code, you can access predefined constants, known as Secrets. We discussed what these are in our previous article, but in brief – Secrets are configuration values that you can store relative to your HubSpot portal. For example, if you have API keys for a third-party service you want to contact as part of your custom code action, then you would want to store your API Key in a Secret rather than storing it in the custom code itself. These Secrets are stored and encrypted securely on HubSpot’s servers. You can define any number of Secrets to be used across your HubSpot portal.

Important:  In your custom code, you will ONLY have access to the Secrets in the dropdown that you have selected, as shown in the screenshot below. If you have not selected the Secret that you want to use, it will not be available to you and will throw an error. The dropdown is multiple select, so you can utilize more than one Secret at a time. In case it isn’t obvious, which it sometimes isn’t, you can add/manage Secrets after opening the “Choose a secret” dropdown.

Screenshot showing how to select a Secret in your custom code

After you have selected which Secrets you wish to have made available to you, you can then access the Secret’s value with your code using environment variables. For example, a Secret with the name “DOTDIGITAL_CREDS_BASE64” is available in the code as process.env.DOTDIGITAL_CREDS_BASE64.

Let's Debug This Thing!

There is a wonderful feature available in custom code which is a native component of Node.JS. This is being able to utilize console.log! Console.log allows you to output debugging information to a buffer that is visible as the custom code is run. You are free to export anything to the log as you wish, with a few exceptions:

  • log has a maximum amount of characters/data that you can log. It is easy to surpass the limit of 4k. If you are attempting to log a large data object, it will be truncated or not display at all.
  • Custom code actions must finish running within 20 seconds and can only use up to 128 MB of memory. Exceeding either of these limits will result in an error. 

Important:  Once you begin utilizing custom code in HubSpot Workflows, it is very important to note that HubSpot does NOT auto-save the contents of the code entered in the text area. If you are making changes, be sure to save frequently so that you don’t lose your changes.

Before we go any further, it is very valuable to enable “Full Screen” editing before attempting to debug your custom code, as shown in the screenshot below.

Screenshot illustrating how to select Full Screen view

To see the output of what you are adding to the console log buffer, you can utilize the “Test action” capability that HubSpot provides. You can trigger the custom code to process for a specific contact and then you can see the changes that were made because of your custom code being triggered.

Screenshot showing how to select the record to be tested

After clicking the “Test” button a popup will appear asking you to confirm that you are aware the contact you are choosing to test with will be edited if the custom code in the workflow is meant to manipulate the data on a contact. In this example, we are not manipulating the contact data. But if we were, it is best to create a test contact to utilize the “Test action” with.

Once your test has run, a new section will appear which displays the status of your test action and the data the test has outputted.

Screenshot showing test results

Within the “Logs” section, you will be able to see all the entries to console.log.

As mentioned earlier, should you attempt logging an object or string which is too large, it will either be truncated or not display at all. You will also see a warning like the following that says “WARNING: The logs for this function have exceeded the 4kb limit.”

Screenshot showing exceeded warning message

Once you have tested your code as much as possible, you’re ready to process your workflow and therefore run your custom code. After your workflow is executed and your custom code is processed, you can again see the entries made to console.log to see the history of actions completed while your code is being executed.  

To see this, go to the “History” tab of the Workflow.

Screenshot showing how to view history

You will then see the “Action logs” for this Workflow. Within this, you should find an entry for the execution of your custom code. When you’ve found this entry, hover over it and click the “Event Details” button to see the information logged upon processing.

Screenshot showing how to select event details

The logs will then be displayed along with much other helpful information on the following screen.

Screenshot illustrating event log detail

We hope that this article has been helpful in providing you the tools to be able to debug custom code from a HubSpot CRM Workflow. If you have any additional questions, please don’t hesitate to reach out to us! We are always happy to help.

More To Explore

b2b auto pay

B2B Auto Pay: Automation Use Cases

Migrating a B2B “Auto Pay” Program Companies migrating to SAP often have daunting challenges to overcome in Accounts Receivable as part of the transition. You might have different divisions running

ArgonDigital | Making Technology a Strategic Advantage