Login

HubSpot Stealth Form Capture

hubspot_form Header Image
Left Arrow
Jeff Bower | 2024-11-11

One of HubSpot’s strengths is that you can use HubSpot forms to gather data. It will do things like track where a user came from, you can use it to track the status of various comments and requests that come through, and you can use it to push data from your application into HubSpot for processing. But this isn’t as easy as you’d think when the data isn’t submitted through an ordinary form or has potentially sensitive information.

The Problems

First of all, the free version of HubSpot is branded and styling is a bit limited. I don’t like this issue at all, but for paid HubSpot customers or people less sensitive to freemium branding than I am, the default HubSpot solution may work fine.

However, using a vanilla HubSpot form for something regarding the app means that you’ll need to somehow bifurcate the data - rework your application so you can process the data in the app as well as sending it to HubSpot. HubSpot forms shine as a “Contact Us” form, but as something to sign up for or an app or to configure a feature may end up putting the CRM in the path of making app changes - not that a great of an idea.

Using a non-HubSpot form is also possible, this will let HubSpot snoop on the data from a form created on your website and record a snapshot once the form is submitted, but it will miss a lot of the validation so a user who clicks Submit early will send the current snapshot of the form to HubSpot. But if they need to correct something, the second submission will not be recorded so you may have invalid or missing data if the initial submission doesn't work as planned.

Non-HubSpot forms are also a bit limited in configurability and sometimes HubSpot gathers too much data, things like password fields could end up in your CRM without encryption. Letting HubSpot snoop on a non-HubSpot form gathering PII or secure data is a massive risk and I've caught issues where a secondary password field would sometimes be added to the form records.

I also don’t have an account creation form. You simply login with your Google account and I use your unique Google login ID as a username without needing to enter in any form data. This obviates the need for any form at all and makes things a bit more complex.

I could push this data via an API call, but the problem with that is it’s not tied to the actions of the currently logged in user. You end up losing attributions and any past information HubSpot has gathered and the contact source is simply "Integration" with no tieback between the user's browser activity and their actual contact information.

The Solution

The solution is actually not that hard and works as a replacement for an API call while preserving the web session data. In essence, what I do is I pull up a HubSpot form in an invisible <div> and then use JavaScript to pass the appropriate values over before I submit it. The code here is part of a HEREDOC definition in PHP, so I escaped $form to \$form and make reference to some PHP variables.

// First load the HubSpot JavaScript<script charset="utf-8" type="text/javascript" src="//js.hsforms.net/forms/embed/v2.js"></script>// Then make HubSpot forms invisible by setting display: none<style>.hbspt-form { display: none;}</style><script>// This function will generate the form in an iframe, this makes it hard to tweak.hbspt.forms.create({ portalId: "{{PORTAL_ID}}", formId: "{{FORM_ID}}", // Once the form is ready, wait 2 seconds and submit it. onFormReady: function (\$form) { setTimeout(\$form.submit(), 2000); }, // When the form is submitted, copy values into the fields from PHP variables, but transposing from a non-HubSpot form is also viable. onBeforeFormSubmit: function (\$form) { \$form.querySelector('input[name="firstname"]').value = '{$LOGIN_DATA['given_name']}'; \$form.querySelector('input[name="lastname"]').value = '{$LOGIN_DATA['family_name']}'; \$form.querySelector('input[name="email"]').value = '{$LOGIN_DATA['email']}'; \$form.querySelector('input[name="eb_login_id"]').value = '{$LOGIN_DATA[‘login_id']}';; \$form.querySelector('input[name="eb_last_login"]').value = new Date().toISOString().slice(0, 10); },});</script>

In the example above, I wait 2 seconds for the form to load (this may be able to be cut down) and then I submit it. Prior to submission, I copy variables I know at the PHP level into the form including the user’s name, email, Google ID, and I populate the Last Login to today’s date.

Parallel Forms

This may not work well for parallel forms in the free version of HubSpot. For the paid version, you can generate a raw HTML form which does not use iframes, here it’s easy to migrate data from the visible form to the invisible HubSpot form since it’s not in an iframe and code like this should work:

$form.querySelector('input[name="firstname"]').value = document.getElementById(‘myRealField’).value;

However, with the free version you’re stuck with the iframe and being able to submit and pass data to an iframe with a different domain is a very tough problem that would probably break a lot of the basics of Internet security.

Instead, it’s much easier to add the HubSpot form to the receiver page that processes the account creation request for the application, at this point you know the data that’s been submitted and you can simply use the code above.

Summary

Getting app data into HubSpot is pretty critical. HubSpot keeps track of how a user found your platform, whether it was a click off LinkedIn or Facebook, an ad, or a webinar. HubSpot figures out how users got to your platform, other applications like Pendo are better suited towards figuring out how they’re using the platform.

By using this invisible form technique you can now pass data from your application into HubSpot without needing to worry about HubSpot grabbing sensitive data or losing the breadcrumbs HubSpot has been recording for you. And, once it’s there, you can set up all sorts of things from newsletters, roadmap announcements, and feature spotlights. Plus, HubSpot will manage the unsubscriptions for you.