HubSpot forms can carry TraceLead's attribution into the contact record as ordinary contact properties. TraceLead never connects to your HubSpot account and holds no HubSpot credentials: the form you embed sends the values to HubSpot itself, alongside the visitor's answers.
Status: tested on every release against HubSpot's rendered markup and its documented form API (a stand-in for HubSpot's embed script, so the test needs no HubSpot account). Not yet verified on a live HubSpot portal; see the integrations ledger.
- 1. Create the contact properties
- 2. Add them to the form as hidden fields
- 3. Install the snippet on the page
- What appears in HubSpot
- Troubleshooting
1. Create the contact properties
In HubSpot, open Settings → Properties, choose the Contact object and create one property per value you want on the record. A sensible set:
| Label | Internal name | Field type |
|---|---|---|
| TraceLead channel | tl_channel |
Single-line text |
| TraceLead source | tl_source |
Single-line text |
| TraceLead medium | tl_medium |
Single-line text |
| TraceLead campaign | tl_utm_campaign |
Single-line text |
| TraceLead landing page | tl_landing_page |
Single-line text |
| TraceLead first visit | tl_first_touch_at |
Single-line text |
HubSpot suggests an internal name from the label; change it to the tl_ name before saving, because an internal name cannot be edited afterwards. Any field TraceLead can fill works the same way (tl_gclid, tl_utm_term, tl_referrer_host and so on; the full list is in Installing the snippet). If you already have properties with other internal names, keep them and change the mapping on your site's page in TraceLead instead.
2. Add them to the form as hidden fields
In the form editor, add each property to the form and turn on Make this field hidden. Leave the default value empty. Publish the form.
3. Install the snippet on the page
Add the TraceLead script tag to the page that embeds the form, as in Installing the snippet, with your consent call. What happens next depends on how HubSpot renders the form.
Forms rendered into the page (raw HTML)
Legacy forms set to raw HTML render their inputs straight into your page. The snippet fills any input named tl_<field> itself, including forms HubSpot mounts after the page loads, and counts the submission as a lead with the form's id (hsForm_<form id>). No extra code is needed.
Forms in an iframe (the new forms editor)
Forms built in HubSpot's new forms editor render inside an iframe from HubSpot's domain, where no script on your page can reach the inputs. HubSpot provides a form API for exactly this. Paste this script before HubSpot's embed code, after the TraceLead tag:
<script>
// TraceLead → HubSpot (forms from the new forms editor). Paste before HubSpot's embed code.
(function () {
var PREFIX = '0-1/tl_';
window.addEventListener('hs-form-event:on-ready', function (event) {
var form =
window.HubSpotFormsV4 &&
window.HubSpotFormsV4.getFormFromEvent(event);
if (!form) {
return;
}
window.tracelead('touch', function (fields) {
if (!fields) {
return;
}
Object.keys(fields).forEach(function (key) {
try {
if (fields[key]) {
form.setFieldValue(PREFIX + key, fields[key]);
}
} catch {
// The form has no property for this field; skip it.
}
});
});
});
window.addEventListener(
'hs-form-event:on-submission:success',
function (event) {
window.tracelead('lead', 'hubspot-' + event.detail.formId);
},
);
})();
</script>
When the form is ready, the script asks TraceLead for the attribution and sets each property through setFieldValue('0-1/tl_<field>', value). After a successful submission it reports the lead to your TraceLead dashboard as hubspot-<form id>; the report carries the form id and the stored attribution, never the visitor's answers. If your internal names do not start with tl_, change PREFIX.
Without consent, TraceLead hands the script nothing, the hidden properties stay empty and the form submits normally. If consent arrives while the form is open, the properties are set at that moment.
What appears in HubSpot
Each new contact created through the form has the TraceLead properties filled with the first touch: for example TraceLead channel paid_search, TraceLead source google, TraceLead campaign spring-launch. Add them to the contact record sidebar, filter lists by them, or build a report of contacts by TraceLead channel. This works on any HubSpot plan, including those without HubSpot's own attribution reports.
HubSpot keeps the value it received at submission. A later form submission by the same contact overwrites the properties with that submission's values, which is still that browser's first touch unless it has expired or the visitor used another device.
Troubleshooting
- Properties stay empty: check that consent was given on the page, that the internal names match (
tl_channel, nottracelead_channel), and that the fields are on the form as hidden fields.tracelead('diagnostics', console.log)in the browser console shows whether consent was given and whether a touch is stored. - Raw HTML forms not filled: the input names in the rendered form must match the site's mapping; inspect the form and look for
name="tl_channel". - Leads counted twice: use the iframe script only for iframe forms. On raw HTML forms the snippet already counts the submission.