Technical Solutions Blog

Posts by:

Robert Ainslie

I love technical problem solving and creating multi-step technical solutions. Outside of work, I enjoy riding bikes.

A step-by-step tutorial: How to Submit to the HubSpot Forms API with JavaScript

This tutorial will walk you through how to submit to the HubSpot Forms API using Javascript. It will use vanilla JS (i.e. no libraries or frameworks) and will provide example code. It will assume that you are familiar with HubSpot, HubSpot Forms, and that the HubSpot Tracking Code is on the page.

Upon completing this tutorial, consider taking this a step further by implementing this same functionality using a front end JS framework like Vue.js, which makes working with returned form data significantly easier.

Note: there are many, many ways to do what this walkthrough will show, this is but one way.

The highlevel steps you will follow include:

  1. Create a form in HubSpot as way to ‘accept’ a form submission
  2. Create a HubSpot custom module to house your form HTML and javascript
  3. Add code to your module step by step
  4. Test and submit!

Troubleshooting note: as you work through this tutorial, if you run code and hit errors, view the JS consolel, and add console.log() statements to output data at different steps.

Create a HubSpot Form

This is pretty straightforward. In HubSpot, create a form and add a few fields to it. Recommended fields include:

  • First name (name: firstname)
  • Last name (name: lastname)
  • Email (name: email)
  • State (name: state)
  • Notes (name: message)

Make note of the ‘database’ names of these fields, the 5 fields above have these listed. (The JS code will take whatever the ‘name’ attribute of a form field is and try to submit to a HubSpot field of the same database name. So, if you want to submit other fields, manually add them to the HTML form in the next step).
Next, make note of the form GUID - it’s the long string in the URL. Save that for later.

Create a custom module

Navigate to the HubSpot Design Manager and create a new custom module. Make the module for use in pages and blogs, but not emails.

Now, let’s create a basic form.
In the HTML + HuBL section of the custom module, add the following basic form HTML:

<form method="POST" id="custom-form">
<input type="text" name="firstname" placeholder="First Name">
<input type="text" name="lastname" placeholder="Last Name">
<input type="text" name="email" placeholder="Email">
<select name="state" placeholder="State">
<option>Massachusetts</option>
<option>California</option>
</select>
<textarea name="message" placeholder="Your Message"></textarea>
<input type="submit" value="Submit Form">
</form>
<div id="thankyou"></div>

In the above form, note the field types, the ‘name’ attributes of the fields, and the submit button.

Next, let’s add the bulk of the functionality with javascript.
This code uses the HubSpot Forms API endpoint documented here.
Before diving into the code, let’s breakdown the necessary steps to properly submit a form to HubSpot:

  1. Get all fields values from the HTML form
  2. For each form field, transform the key/value pair data into the JSON format expected by the HS Forms API
  3. Get browser cookies and grab the value of the HubSpot tracking cookie, named hubspotutk
  4. Create a context object in the JSON to be submitted that includes the hubspotutk, the page URL and page name
  5. If we were to be GDPR compliant, build a legal consent section for permission to process data and sign up for email subscriptions (this tutorial is skipping this step)
  6. Create an event listener that listens for a form submit event, and then submits the form via an AJAX request (using the Fetch API in our case)

As we proceed, most of these steps are broken down into several functions to allow for better code portability. Some functions call previous functions, so these might not necessarily work independently. Copy and paste the following code as we move through these steps, and read the code, understanding the gist of each step.

  1. Get all field values from the HTML form
function formFieldsToHSJSON(form) {
let fieldArray = [];
let formData = new FormData(form);
for (let field of formData) {
let values = {
"name": field[0],
"value": field[1]
}
fieldArray.push(values)
}
return fieldArray;
}

This code expects a form field element as an argument, then iterates every field, building JSON in the field formatting expected by the HubSpot Forms API. It returns data like this:

[{"name":"firstname","value":"Your Name"}]

In our later usage, it will be used like this (don’t copy/paste this yet):

var form = document.querySelector('#custom-form')
formFieldsToHSJSON(form)
  1. Next, let’s add a helper function to get browser cookies by name. Copy/paste:
function getCookie(name) {
var value = "; " + document.cookie;
var parts = value.split("; " + name + "=");
if (parts.length == 2){
return parts.pop().split(";").shift();
}
};

This function will be used like:

var hubspotUtk = getCookie('hubspotutk')
  1. Let’s use this function in our next function - one to actually build the context object in the eventual form submission data. Copy/Paste this function
function buildHSContext() {
let hsContext = new Object()
hsContext.hutk = getCookie('hubspotutk');
hsContext.pageUri = window.location.href;
hsContext.pageName = document.title;
return hsContext
}

This function returns an Object and uses the getCookie function, and also grabs the page title and page URL.

  1. Now let’s add a function that prepares all of the submission object data that the form will submit. This function combines and calls all of the other functions so far! Notice how it creates a new Object, then adds object keys for the submission Date, the field data, and the context object.
function prepareHSFormSubmission(form) {
var submissionData = new Object()
submissionData.submittedAt = Date.now()
submissionData.fields = formFieldsToHSJSON(form)
submissionData.context = buildHSContext()
return submissionData
}
  1. Next up - there are multiple ways to make an HTTP request from the browser, and one of the more modern ways is using the fetch API. Learn about fetch. Note: Fetch has NO browser support in IE, but is supported in Edge. Oh well. If you wanted better cross browser compatibility, you could implement this using XMLHttpRequest, but this tutorial isn’t.

This handy function makes it slightly easier to use Fetch for POST reqeusts (thanks Mozilla MDN!)
Copy/paste it to your module!

async function postData(url = '', data = {}) {
// Default options are marked with *
const response = await fetch(url, {
method: 'POST', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, *cors, same-origin
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, *same-origin, omit
headers: {
'Content-Type': 'application/json'
// 'Content-Type': 'application/x-www-form-urlencoded',
},
redirect: 'follow', // manual, *follow, error
referrerPolicy: 'no-referrer',
body: JSON.stringify(data) // body data type must match "Content-Type" header
});
return response.json() // parses JSON response into native JS objects
}
  1. For the final function, this will be used to actually submit the data. It calls the postData\ function from above, then, takes the inline thankyou message and puts that message in an element with ID thankyou Copy/paste this code:
function submitHSForm(hsFormURL, data) {
postData(hsFormURL, data).then(data => {
if(data.inlineMessage){
// Set an inline thank you message
document.querySelector("#thankyou").innerHTML = data.inlineMessage
}
});
}
  1. That’s it for defining the functions. Now, let’s put those functions to work! Add all of the code below, customizing for your HubSpot portalID and your form GUID. Read the code comments for more detail. Copy/paste, then edit!
var form = document.querySelector('#custom-form') //alter for your forms ID or CSS selector
form.addEventListener('submit', function (event) {
event.preventDefault();
var baseSubmitURL = 'https://api.hsforms.com/submissions/v3/integration/submit'
// Add the HubSpot portalID where the form should submit
var portalId = 'YOUR-HUBSPOT-PORTAL-ID'
// Add the HubSpot form GUID from your HubSpot portal
var formGuid = 'd9afc0c5-xx-yy-zz'
var submitURL = `${baseSubmitURL}/${portalId}/${formGuid}`
var formData = prepareHSFormSubmission(form);
submitHSForm(submitURL, formData)
});

It does 6 things:

  1. Grabs the form element and all nested inputs via document.querySelector. You can edit the #custom-form to be a different selector if you altered the form ID.
  2. Creates an event listener that waits for a submit event on the form element from the code defined in the previous step
  3. On submit, it ‘prevents default’ - the native behavior of a form submision is to redirect the browser through the submission URL. This is stopped so that the user stays on the same web page and just gets the inline thank you message. This is a common thing to do when working with AJAX form submissions.
  4. Creates a set of variables for the submission URL, the HubSpot Form GUID, and the portal ID
  5. Then, prepares all of the Form data by calling the prepareHSFormSubmission function, which returns the JSON body to make the API call. Remember that this function does several things - gets the form field data, transforms it, gets the HubSpot usertoken cookie value
  6. Runs the submitForm function, POSTing data to the Forms API, once there is a successful submission, the Thank you message is returned and rendered on the page.

Now, save your custom module, add it to a page, and submit the form to test the submission!

Read More

Creating a HubSpot Timeline API Integration - Saving Google Sheet Rows as HubSpot Timeline Events using Integromat

Written by Rob Ainslie | Last Updated 6/6/2020
Estimated time to complete: 1 hour
Watch an overview of the tutorial components and finished product

Overview and Setup

The overall goal of this tutorial is to show how to use Integromat to send data to the HubSpot Timeline API. This implementation has many use cases, so for this tutorial, we will use Google Sheets as a sample app trigger, but the integration trigger could be any app or other integraton source.

For the specific use case, imagine a scenario where webinar attendees are added to a Google Sheet and you want to log that on the HubSpot Timeline. The benefit of the HubSpot Timeline API is that you can maintain the many-to-one relationship of ‘webinar attendance’ to Contact.

Integromat is a low-cost but powerful integration platform as a service (iPaaS), which this tutorial uses because of one particular feature that is available in their free tier that is typically only available in most other Enterprise grade iPaaS systems - that feature is the ability to create an arbitrary oAuth connection. Why is that important? Read on!

The HubSpot Timeline API is a ‘platform’ level API that is typically only available to HubSpot integrators that create developer apps. All developer-based apps must utilize oAuth as the authentication method (as opposed to key based authentication using a specific portal’s HAPI key). oAuth is great because it allows a user to authorize an app to their portal without sending passwords or one-size-fits-all API keys directly to the integrator. Though oAuth is more secure than using integration keys, it also requires overhead and technical know-how to set up. Luckily, Integromat takes care of most of the heavy lifting, allowing us to easily use powerful APIs without having to code at all! Nice!

This tutorial will follow these steps. Read on for the specifics:

  1. Create an Integromat account. Sign up here
  2. Create a new Integromat Scenario and authorize Google Sheets
  3. Create a HubSpot Developer account, create an app, then set up an oAuth based connection in Integromat
  4. Create the scaffolding for the data transfer (Google Sheet with columns, HubSpot Timeline Event template)
  5. Finish setting up the Integromat Scenario
  6. Test by adding data to the Google Sheet and seeing that data in HubSpot

Create an Integromat Account

Easy peasy - sign up for a free Integromat account. I recommend using Google Sign In, but that’s up to you. See how their pricing works here - they offer a ton of functionality and limit based on data usage, not on features. Feel free to do their onboarding walkthrough, or skip.

Create a new Integromat Scenario

A Scenario is where we setup the app connections, logic and steps of our integration. Scenarios can be scheduled or triggered, which is helpful if there are certain times when an integration needs to poll an API for changes. Integromat also supports webhook based triggers where they provide you a webhook URL to send data to. This could be used in something like a HubSpot Workflow, where you webhook the data from the Workflow to the integration scenario. But for now, we’ll use Google Sheets as the trigger app. Let’s create the Scenario

We will create the initial shell of the integration scenario and the starting condition

  1. On the Integromat Dashboard > open Scenarios > Create New Scenario > Add Google Sheets. Note: the following gif uses an already authorized Sheets account, you will do that in the next step Create Integromat Scenario
  2. Click on the Starting trigger like in the gif above. Authorize Google Sheets to your G Suite Account.
  3. Since our use case example is a list of webinar attendees, stage a sheet with this sample data in your Google Sheets account. Recommend making a copy of this sheet for usage as a sample data set.
  4. On your starting trigger, choose Google Sheets and then choose the ‘Watch rows’ trigger and complete the conditions, choosing the sheet you just cloned. Choose Google Sheet as trigger Note: the screenshot alters the ‘limit’ parameter, for this exercise, choose a limit larger than 3

Create a HubSpot Developer Account and prepare the oAuth connection

Due to certain HubSpot APIs being limited to oAuth as their authentication method (such as the Timeline API, CRM Cards and Webhooks), we’re forced to create our own app to authenticate to HubSpot. Integromat does have an out-of-the-box HubSpot connector that you can authorize for a slew of other standard integration triggers and actions, however we’re not using them (or the HubSpot CRM app connection) in this tutorial.

  1. Sign up for a HubSpot Developer Account, or log-in to your existing account
  2. Create a new app and name it like “Custom Webinar Attendance” Create HubSpot App
  3. Familiarize yourself with the resulting screen. We’ll use the ‘Auth’ tab and the ‘Timeline events’ section HubSpot app auth and timeline highlight

Next, we’ll create the HTTP connection using oAuth 2.0 as the authorization method. This set of steps is one of the features that sets Enterprise grade iPaaS from others. The gist is that we’ll create a new HTTP connection in Integromat and provide it several configuration options and secret keys provided by your HubSpot developer portal app. The HubSpot app needs to be configured for the correct scopes (scopes grant the integration access to only certain data in HubSpot).

  1. In Integromat, on the bottom toolbar, add a new app connection. Find and choose HTTP (conveniently right above HubSpot CRM!) Add HTTP Connection
  2. Click into the widget and choose “Make oAuth 2.0 Request” Make oAuth 2.0 Request
  3. ‘Add’ a new connection Add new oAuth connection

Most of the details to add for the oAuth connection are provided to us by our HubSpot app. Navigate back to your HubSpot Developer portal and click to the ‘Auth’ tab Click to the Developer app auth tab

  1. Scroll down a bit and start by adding a redirect URL. Integromat provides this for us in this case. Paste in www.integromat.com/oauth/cb/oauth2
  2. Then add scopes for contacts, oAuth, timeline and integration-sync and then Save changes. Add redirect and scopes.
    Next, you will need the client id and client secret for your app auth in Integromat. Copy them one at a time and paste in Integromat. Note: the client secret should never be shared publicly - this is the hidden key that should only be used in server-side applications.
    HubSpot Client ID and Client Secret

To complete the oAuth connection, in Integromat add the following:

  • Name your connection, it can be anything, but maybe HubSpot Webinar App
  • Authorize URI: https://app.hubspot.com/oauth/authorize
  • Token URI: https://api.hubapi.com/oauth/v1/token
  • Add scopes of contacts, oauth, timeline, and integration-sync - these must exactly match the scopes chosen in your HubSpot app!
  • Copy and paste the Client ID and Client Secret from your HubSpot App
  • Click Show Advanced Settings and change Scope separator to Space
  • When complete, the Create a connection dialog should look like these two screenshots: Authorize and Token URI Scopes
  • Click Continue and you will be prompted to log-in to HubSpot, and then to Grant access to the app - choose the portal you want the integration installed into - that could be your personal portal or a test portal. HubSpot Portal picker HubSpot app authorization

Congrats, you just setup an oAuth app in a HubSpot portal! Celebrate!

Setup the Timeline Event Data Structure in HubSpot and Integromat

Next, we will create a new Timeline Event type in our HubSpot app. Once created, you will see additional segmentation options in HubSpot Lists, Workflows and Lead Scoring

  1. Navigate to the ‘Timeline Event’ section of your HubSpot app and create New event type
  2. Name your event type
  3. On the ‘Content’ tab, choose Target Record Type of Contact
  4. Flip to the ‘Data’ tab and Add new property. Give a field name of webinar_name and a data type of String
  5. Add 3 more properties:
  • Property name: webinar_date, data type: Date
  • Property name: webinar_presenter, data type: String
  • Property name:webinar_category, data type: Enumeration, options: lead_gen, tutorial and customer_marketing

Once all properties are added, the screen should look like:

  1. Flip back to the Content tab and edit the Header template and Detail template. Copy and paste the following:
    Header template:
    Webinar attendance: {{webinar_name}} on  {{#formatDate webinar_date format="short"}}{{/formatDate}}
    
    Detail template:
    This event was created on {{#formatDate timestamp}}{{/formatDate}}
    
    **Webinar Name**: {{webinar_name}}
    
    **Webinar Category**: {{webinar_category}}
    
    **Webinar Presenter**: {{webinar_presenter}}
    
    **Webinar Date**: {{#formatDate webinar_date format="short"}}{{/formatDate}}
    
  2. Click Create. Your Timeline Event Type should look like this:

Whew. Now, we’re really ready to rock and roll

Navigate back to Integromat, and in the template details for the ‘Make a OAuth 2.0 request’, input the following:

  • URL: https://api.hubapi.com/integrators/timeline/v3/events
  • Method: POST
  • Body type: Raw
  • Content type: JSON (application/json)
  • For Body, copy and paste the following. You will need to edit the ID in the eventTemplateId to match the event ID in your HubSpot app (screenshot below)
    {"eventTemplateId":"1005087","email": "{{1.`0`}}","tokens":{"webinar_name":"{{1.`1`}}","webinar_date":"{{formatDate(1.`2`; "x")}}","webinar_category":"{{1.`4`}}","webinar_presenter":"{{1.`3`}}"}}
    
    Copy the evetTemplateId and replace in the above body:
    Note: When you paste the body into the Integromat request, make sure there are no spaces or unintentional linebreaks - the formatting of the raw request can be finnicky - the above should work. (Integromat does have a handy ‘JSON’ action step to better format common JSON bodies, but we aren’t using it here).
    The finished request in Integromat should resemble this:

Review the pieces and run the integration scenario

Before running your integration scenario, double check these pieces:

  1. Authorized Google Sheets account
  2. Starting Google Sheet’s trigger is using a copy of the provided sheet
  3. The HTTP Request action is authorized via oAuth 2 to an actual HubSpot portal
  4. The Event type in your app has all of the proper properties and Event templates
  5. The HTTP Request has the right settings to make the POST request

Assuming all of the pieces are in place, click the ‘Run Once’ button in the bottom right of your Integromat Scenario and then view the output logs:

If your output received a 201 response code, go find the contacts in your HubSpot portal and inspect the timeline (you may need to select the integration in the Contact Timeline filters):

If you need to troubleshoot, copy and paste new rows into the Google Sheet and then run the scenario again, using the output logs to troubleshoot.

You did it! You integrated to the HubSpot Timeline!

Some random final notes:

  • The Integromat Google Sheets trigger iterates every new row for you, however, some apps and data need to have a step that has an iterator, running a loop for every record (and making one HTTP request per item). Understanding your data sources and outputs is critical
  • This tutorial provided one sample use case, but the possibilities are vast - what would you solve by sending data to the Timeline?
  • One feauture of the Timeline API we didn’t use is the ability to copy timeline event data into a property. This way, you could send a timeline event, copy an event property to a contact property, and then personalize on that data. The property would be overwritten everytime there is a new event though.
  • In the future, Custom Objects may be a better fit for this type of data modeling, but it’s a case-by-case basis on whether something is an ‘Event’ or a custom object record
  • Integromat is a great service, but for large data volume jobs, it might not be the best option due to their pricing as well as the fine control you may need. However, for smaller customers or for less data-intensive jobs, it can be a great option instead of tools like Zapier or more expensive alternatives like Workato or Mulesoft.
Read More