Most B2B tracking stops at the form submission. The sales team then spends a week discovering that half the submissions were students, competitors and vendors, and the ad platforms never find out. They keep optimising for the kind of person who fills in forms.
This guide covers two layers. First, tracking HubSpot form submissions properly in Google Tag Manager. Second, sending Sales Qualified Lead stage changes from HubSpot back to Google Ads and Meta through server-side GTM, so the platforms learn from qualified pipeline.
Part 1: HubSpot Form Submission Tracking
How HubSpot forms confirm a submission
Embedded HubSpot forms post messages to the parent page. The one we care about has type equal to hsFormCallback and eventName equal to onFormSubmitted, with the form GUID in the data. Newer HubSpot form embeds also emit a hs-form-event:on-submission:success event. The listener below handles both.
Step 1: Listener tag
Create a Custom HTML tag named Listener - HubSpot Forms on All Pages:
<script>
(function () {
var lastEmail = '';
window.dataLayer = window.dataLayer || [];
// Capture the email as it is typed, since HubSpot clears the form on success.
document.addEventListener('input', function (e) {
var t = e.target;
if (t && t.name === 'email' && t.closest && t.closest('form.hs-form')) {
lastEmail = (t.value || '').trim().toLowerCase();
}
}, true);
function pushSuccess(formId) {
window.dataLayer.push({
event: 'form_submit_success',
form_tool: 'hubspot',
form_id: formId || '',
user_email: lastEmail || undefined
});
}
window.addEventListener('message', function (e) {
var d = e.data;
if (!d || typeof d !== 'object') { return; }
if (d.type === 'hsFormCallback' && d.eventName === 'onFormSubmitted') {
pushSuccess(d.id);
}
});
window.addEventListener('hs-form-event:on-submission:success', function (e) {
var detail = e.detail || {};
pushSuccess(detail.formId || (detail.form && detail.form.id) || '');
});
})();
</script>
Step 2: Capture the Google click ID on the contact
Add a hidden field named gclid to the HubSpot form, create a matching contact property, and populate the field from a first-party cookie that stores the gclid URL parameter for 90 days. HubSpot also captures its own hutk cookie, and HubSpot's Google Ads integration can store gclid automatically when connected. Either way, make sure the click ID ends up on the contact record. Everything in Part 2 depends on it.
Step 3: Variables, trigger and tags
Data Layer Variables for form_id and user_email. Custom Event trigger form_submit_success with a form_tool equals hubspot condition. Fire:
- GA4 Event
generate_leadwithform_id. Mark as a key event. - Google Ads Conversion for the lead, with a User-Provided Data variable from
user_emailfor Enhanced Conversions and the Conversion Linker on All Pages. - Meta Lead with advanced matching.
If you run server-side GTM, send these through your server container rather than directly from the browser, so the same endpoint handles both the form fill and the SQL and can deduplicate by event ID.
Part 2: Sales Qualified Lead Tracking
The flow
- Sales changes the contact's lifecycle stage to Sales Qualified Lead in HubSpot.
- A HubSpot workflow fires a webhook to your server-side GTM container.
- The server container receives the contact's email, gclid and timestamp.
- It sends an offline conversion to Google Ads and a server event to Meta.
Step 4: Create the conversions in the ad platforms
In Google Ads, create a conversion action named SQL with source Import from clicks, or use Enhanced Conversions for Leads so it can match on email as well. Set a 90 day window and a value that reflects what an SQL is worth to you.
In Meta Events Manager, decide on an event name. A custom event such as QualifiedLead, or the standard Lead event with a higher value, both work. For Conversion Leads optimisation, Meta expects a CRM integration or Conversions API events that reflect lead stages.
Step 5: Build the HubSpot workflow
Create a contact-based workflow with the enrolment trigger Lifecycle stage is any of Sales Qualified Lead. Add a Send webhook action with method POST to your server-side GTM endpoint, for example https://sgtm.yourdomain.com/hubspot-sql. Include the contact's email, the gclid property, the lifecycle stage and the current timestamp in the request body. Add a shared secret header so the endpoint only accepts requests from HubSpot.
Step 6: Receive the webhook in server-side GTM
In the server container, add a Client that listens on the webhook path and claims the request. Parse the JSON body into event data: event_name of sql, email, gclid, conversion_time. Reject requests without the secret header.
Step 7: Send to Google Ads and Meta
- Google Ads. Use the Google Ads Offline Conversion tag in the server container, or the Google Ads conversion tag with Enhanced Conversions for Leads enabled, mapping the gclid, the conversion time and the hashed email to the SQL conversion action.
- Meta. Use the Meta Conversions API tag with event name
QualifiedLead, the hashed email as user data, and an event ID built from the contact ID and stage so a repeat webhook is deduplicated.
Step 8: Test end to end
- Submit the HubSpot form as a test contact after arriving on the site with
?gclid=test123. Confirm the form fill conversion fires in GTM Preview and that the contact in HubSpot hastest123in the gclid property. - Manually set the contact's lifecycle stage to SQL. Confirm the workflow ran and the webhook returned 200.
- In the server container's Preview, confirm the request was claimed, the data parsed and the Google Ads and Meta tags fired.
- In Google Ads, check the conversion action's Diagnostics or upload history after a day. In Meta Events Manager, check the Test Events tab with a test event code.
What Changes Once SQLs Flow
Set SQL as the primary conversion in Google Ads and demote the form fill to secondary after you have 30 or more SQLs in a 30 day window. In Meta, test a campaign optimised toward the QualifiedLead event. Both platforms will shift spend toward the audiences and keywords that produce leads your sales team actually calls back.
Common Problems
- Webhook arrives without gclid. The hidden field is not populated, usually because the cookie is not set when visitors arrive from channels other than Google Ads. Fall back to Enhanced Conversions for Leads matching on email.
- Duplicate SQL conversions. The workflow re-enrols contacts. Set it to enrol once, and use a deterministic event ID for Meta.
- Form fill fires but no email captured. The input listener did not see the field. HubSpot forms rendered in an iframe keep the inputs inside the frame; in that case rely on Option B from the Calendly guide, a server-side lookup, or HubSpot's own integration for Enhanced Conversions.
Outcome
Your ad platforms stop learning from form fills and start learning from the leads sales wants. In most B2B accounts that shifts spend noticeably within a month, and it gives marketing and sales the same definition of a good lead for the first time.
