Hi, I am using GTM and GA4 on my website to gather analytics data. I have embedded a hubspot form that i created a hidden block on labeled 'GA Client ID' (internal name is ga_client_id) in hopes that a GTM/javascript setup will be able to inject the client id data into that block so when someone submits the form their client id is carried over into hubspot too. currently i am using a custom html tag in GTM that fires after my config tag triggered on all pages. The tag is firing fine but the data is not being injected and im getting a 403 error on the form too. Here is the script:
<script> function injectGAClientId() { if (typeof gtag !== 'function') { // Retry after 300ms if gtag isn't ready setTimeout(injectGAClientId, 300); return; }
Hello! Here are some things I would try testing out:
1. Double check the form values
What to verify Why it matters Where to fix
Contact property exists & is allowed in forms
A field won’t render (even hidden) unless the underlying property’s Show in forms, pop-up forms, and bots option is on
Settings → Properties → ga_client_id
Field is not marked “Required”
Required fields can’t be hidden; HubSpot blocks the form and returns a 403-style error
In the form editor
It really is on the live form
Progressive / conditional logic can hide a field you thought was there
Test on the form’s standalone URL
Quick test: open the form’s standalone link, inspect element, and confirm you see <input name="ga_client_id" type="hidden">.
2. Try sending the value using v4 HubSpot APIs
The embed script already gives you two hooks:
hbspt.forms.create({
portalId: '243078151',
formId : '29239f66-0317-4f23-9fb5-879021dd5bca',
target : '#hubspot-form-container', /* fires once the form markup is in the DOM */
onFormReady: function(form) { pushGAClientId(form); // set default so the visitor sees the right value }, /* fires *just before* HubSpot packages the payload */
functionpushGAClientId(form){ const cid = getGA4ClientId(); // your cookie-parsing helper
if(!cid) { return; }
/* safest method: HubSpot Forms v4 API */
if(window.HubspotFormsV4){ HubspotFormsV4.getFormFromEvent && HubspotFormsV4.getFormFromEvent({target:form}) .setFieldValue('ga_client_id', cid); // programmatic set }else{ /* fallback: vanilla DOM + synthetic change event */const input = form.querySelector('input[name="ga_client_id"]'); if(input){ input.value = cid; /* tell HubSpot the field really changed */ input.dispatchEvent(newEvent('input', {bubbles:true})); // HubSpot listens for this } } }
Why this version works better
Runs twice – once when the form hits the page, again right before submission, so late-loading cookies don’t get missed.
Uses HubSpot’s own API so the value is guaranteed to persist even if the DOM is re-rendered.
Fires a proper input event when you fall back to direct DOM editing (HubSpot looks for it).
3. Figuring out the 403 error.
A 403 from the forms endpoint usually means something is blocking the request before it ever gets to GA code:
Likely cause Quick diagnostic Fix
Script conflict / altered embed code
Compare your page source vs. the official embed in Marketing → Forms → Share → Get Embed Code (DiffChecker tip from HubSpot docs)
Re-paste the untouched embed snippet
Hidden “required” field or missing email
Submit the form’s stand-alone URL in a clean browser profile. If it works there, the page or a hidden field is the culprit (general troubleshooting flow)
Remove the requirement or expose the field
Region mismatch
js-na2.hsforms.net ↔ region:"na2" is correct. If you ever switch to the default host (js.hsforms.net) make sure region is na1.
Keep region & script host in sync
4. Testing it out
Open the form’s standalone link in an incognito window.
Pop open DevTools → Network tab, filter for submit-form.
Submit once and verify you see ga_client_id=<value> in the payload and a 204 No Content response (success).
If you still see 403, temporarily remove all custom GTM tags, retest, and add back one by one.
Once the hidden property is legit, the field isn’t secretly “required,” and the value is set via the API (or dispatched input event), that GA4 client ID should glide straight into the contact record on every submission.
Holler if anything still misbehaves — happy to dig deeper. Good luck! 👋
Hello! Here are some things I would try testing out:
1. Double check the form values
What to verify Why it matters Where to fix
Contact property exists & is allowed in forms
A field won’t render (even hidden) unless the underlying property’s Show in forms, pop-up forms, and bots option is on
Settings → Properties → ga_client_id
Field is not marked “Required”
Required fields can’t be hidden; HubSpot blocks the form and returns a 403-style error
In the form editor
It really is on the live form
Progressive / conditional logic can hide a field you thought was there
Test on the form’s standalone URL
Quick test: open the form’s standalone link, inspect element, and confirm you see <input name="ga_client_id" type="hidden">.
2. Try sending the value using v4 HubSpot APIs
The embed script already gives you two hooks:
hbspt.forms.create({
portalId: '243078151',
formId : '29239f66-0317-4f23-9fb5-879021dd5bca',
target : '#hubspot-form-container', /* fires once the form markup is in the DOM */
onFormReady: function(form) { pushGAClientId(form); // set default so the visitor sees the right value }, /* fires *just before* HubSpot packages the payload */
functionpushGAClientId(form){ const cid = getGA4ClientId(); // your cookie-parsing helper
if(!cid) { return; }
/* safest method: HubSpot Forms v4 API */
if(window.HubspotFormsV4){ HubspotFormsV4.getFormFromEvent && HubspotFormsV4.getFormFromEvent({target:form}) .setFieldValue('ga_client_id', cid); // programmatic set }else{ /* fallback: vanilla DOM + synthetic change event */const input = form.querySelector('input[name="ga_client_id"]'); if(input){ input.value = cid; /* tell HubSpot the field really changed */ input.dispatchEvent(newEvent('input', {bubbles:true})); // HubSpot listens for this } } }
Why this version works better
Runs twice – once when the form hits the page, again right before submission, so late-loading cookies don’t get missed.
Uses HubSpot’s own API so the value is guaranteed to persist even if the DOM is re-rendered.
Fires a proper input event when you fall back to direct DOM editing (HubSpot looks for it).
3. Figuring out the 403 error.
A 403 from the forms endpoint usually means something is blocking the request before it ever gets to GA code:
Likely cause Quick diagnostic Fix
Script conflict / altered embed code
Compare your page source vs. the official embed in Marketing → Forms → Share → Get Embed Code (DiffChecker tip from HubSpot docs)
Re-paste the untouched embed snippet
Hidden “required” field or missing email
Submit the form’s stand-alone URL in a clean browser profile. If it works there, the page or a hidden field is the culprit (general troubleshooting flow)
Remove the requirement or expose the field
Region mismatch
js-na2.hsforms.net ↔ region:"na2" is correct. If you ever switch to the default host (js.hsforms.net) make sure region is na1.
Keep region & script host in sync
4. Testing it out
Open the form’s standalone link in an incognito window.
Pop open DevTools → Network tab, filter for submit-form.
Submit once and verify you see ga_client_id=<value> in the payload and a 204 No Content response (success).
If you still see 403, temporarily remove all custom GTM tags, retest, and add back one by one.
Once the hidden property is legit, the field isn’t secretly “required,” and the value is set via the API (or dispatched input event), that GA4 client ID should glide straight into the contact record on every submission.
Holler if anything still misbehaves — happy to dig deeper. Good luck! 👋
2. can you provide full code for both - what should be in my GTM tag custom HTML and what should be in my sites elementor HTML? I've tried everything and still nothing.