GoHighLevel install
TrueMetriks tracks pageviews, leads, and purchases from your GoHighLevel funnels and forwards them to every ad platform you have connected. Here is how to set it up.
Set this up with AI
Copy this prompt into ChatGPT, Claude or whichever AI you use. It carries this whole guide, the pages it depends on, and a link to every video, so it can walk you through it one step at a time.
Video guides
Install GoHighLevel tracking
Payment plan and subscription automation
Custom events for forms and calendars
Copy the tracking snippet from your dashboard
In your TrueMetriks dashboard, go to Settings > Tracking, pick the GoHighLevel tab, and click Copy on the Tracking snippet.
Showing setup for GoHighLevel
Install on GoHighLevel
One snippet picks up GHL form submissions, calendar bookings, and purchases automatically.
TRACKING SNIPPET
<script async src="https://q.yourdomain.com/t/s-ghl/abcd1234.js"></script>
GoHighLevel: Funnel settings → Tracking Code → Head. Paste, save, then test a form submission.
Checkout URL pattern
Checkout URL pattern. This is the word the tracker looks for in your URL to identify a checkout page. The default is checkout. If your funnels use a different word in their checkout URLs, change it in this field and click Save.
The card also has three expandable options - one for payment plans and subscriptions, one for firing custom events for your forms and calendars, and one for Typeform or Calendly. The first two are covered below.
Open the funnel in GoHighLevel
In the GoHighLevel left sidebar, click Sites.
At the top of the Sites page, click the Funnels tab.
Funnels
Create and manage funnels to generate leads, appointments and receive payments.
Click the funnel you want to track. You will repeat this for every funnel in your account - the snippet is the same.
Paste the snippet into Head tracking code
Inside the funnel, click the Settings tab. Scroll down to Tracking & scripts and paste the snippet you copied into the Head tracking code field. Then click Save.
← Back
Webinar Replay
Tracking & scripts
Add third-party tracking and analytics scripts to your funnel.
Head tracking code
Repeat for each funnel.
Turn OFF Optimize JavaScript
Stay in the funnel's Settings tab and scroll down to Performance & compliance. Make sure Optimize JavaScript is switched off.
Performance & compliance
Improve performance and meet privacy requirements.
Image Optimization
All images used in this funnel will be optimized for speed and served via CDN with caching enabled.
Optimize JavaScript
Custom javascript and html codes added will be lazy loaded for better page speed and performance.
GDPR compliant fonts
All pages in the funnel will be using GDPR compliant fonts instead of Google Fonts.
This one setting breaks tracking, and it is switched on per funnel, so a funnel that worked yesterday can stop working the moment somebody turns it on.
What it does to you. GoHighLevel's own description is "custom javascript and html codes added will be lazy loaded". Your tracking snippet is custom JavaScript, so it gets lazy loaded with everything else, and instead of running as the page opens it runs many seconds later. On a real customer funnel we measured the snippet loading 10,628ms after the page opened. With the setting off, the same funnel loaded it in 401ms.
What breaks in those ten seconds. The visitor is on your page and TrueMetriks is not running yet, so:
- Plenty of people land, read, and click through before the tracker ever starts, and those visits are never recorded at all.
- A visitor who moves on quickly can be counted as two separate people, because the click that identified them arrived before the tracker was ready. Their landing page and their thank-you page end up in two different sessions.
- The ad click id can be missed, so the sale is not credited to the ad that produced it, and the conversion sent back to Facebook, Google Ads or TikTok carries no click id.
None of this looks like a broken install. The snippet is in the right place and the funnel works fine, you just get thin, split, badly attributed data.
Leave it off and keep it off. Check it on every funnel you track, and check it again if your numbers suddenly drop or your sessions start splitting in two. Image Optimization and GDPR compliant fonts in the same panel do not affect tracking, so set those however you like.
Payment plans and subscriptions (webhook)
If you sell payment plans or subscriptions, add one GoHighLevel automation that POSTs to the TrueMetriks webhook URL each time a payment lands.
Grab the webhook URL
Back in your TrueMetriks dashboard, on the same GoHighLevel install card, expand Click here if you have payment plans or subscriptions and click Copy on the webhook URL.
If you sell payment plans or subscriptions, you need to add a webhook in your GoHighLevel automation to track these payments.
In GoHighLevel, go to Automation, create a new workflow with a Payment Received trigger, then add a Webhook action that POSTs to the URL below.
WEBHOOK URL
https://q.yourdomain.com/api/webhooks/ghl/abcd1234efgh5678ijkl90mn
You can also read the full guide by clicking the Open install guide button below.
Open Automation in GoHighLevel
In the GoHighLevel left sidebar, click Automation.
Create a workflow from scratch
Top right, click + Create Workflow, then + Start from Scratch in the dropdown.
Add the Payment Received trigger
In the new workflow, add a trigger and pick Payment Received from the list. Leave the trigger name as Payment Received (or change it if you want), then click Save Trigger.
Payment Received
Activates when a payment record posts successfully.
CHOOSE A WORKFLOW TRIGGER
WORKFLOW TRIGGER NAME
Add the Webhook action
Below the trigger, add an action and pick Webhook (the normal one - not Custom Webhook). Configure it like this:
- Method:
POST - URL: paste the webhook URL you copied from your dashboard
Then fill in CUSTOM DATA with the keys in the table below and click Save Action. Leave Headers empty.
Fill in Custom Data on every workflow, including one built on GoHighLevel's own Payment Received trigger, and always map product to the merge field that holds the product name. Payment Received does send the amount, currency and transaction id on its own, but it identifies the product by an internal id rather than by its name, so without that one mapping your reports show a long code instead of what was sold.
Webhook
Fire a webhook containing the contact's details.
ACTION NAME
METHOD
URL
Fill in Custom Data with these exact keys
The key names on the left are not free-form: TrueMetriks reads the sale from these keys, so they must be spelled exactly as shown. This matters most on a workflow that carries no standard payment data at all, such as one started by an Inbound webhook trigger (your payment processor, a Zap, or your own backend posts into GoHighLevel) where you build the sale yourself from custom values, but the same keys apply on every workflow.
METHOD
URL
CUSTOM DATA
These custom key-value pairs will be included along with the standard data
Add another item
The left column is the key (fixed, copy it exactly). The right column is whichever merge field in your workflow holds that piece of data.
| Key | Required | What the value must contain |
|---|---|---|
email |
Required | The buyer's email address. This is how the sale is matched to the person and their ad click. |
amount |
Required | The amount of this one payment, as a plain number (1995 or 1995.00). Currency symbols and thousands separators are accepted ($1,995.00). |
currency |
Required | The 3-letter currency code, for example USD, GBP, EUR. If you leave it out, USD is assumed. |
product |
Required | The name of what was bought, for example 1-1 Coaching Extension. This is the product name shown in your reports. If you leave it out, or map it to a product or price id instead of the name, your reports show that code instead of the product and any Facebook custom conversion that matches on the product name will not fire. |
order_id |
Recommended | The unique id of this payment or transaction. It is what stops the same sale being counted twice. If you leave it out, TrueMetriks generates one. |
first_name, last_name, phone |
Optional | Extra buyer details, which improve the match rate on Facebook and TikTok. |
Two rules that stop wrong revenue
1. amount must never arrive empty. If the merge field you mapped to amount has no value for that payment, TrueMetriks falls back to the next money-like field in the payload and can record that number as your sale.
2. Send only one money field. Delete every other key that holds a number of money from the Custom Data list, for example revenue, total, cash_collected, payments, or a lifetime-value field. A running total is not the sale. This is the single most common cause of a purchase showing a much larger figure than the payment that was actually taken: a workflow mapped amount to a field that came through empty, and a revenue key next to it carried the customer's total cash collected to date, so that total was recorded as the sale.
Keep the list to the keys in the table above and the amount in your reports will always be the amount that was charged.
Publish the workflow
Top right, flip the toggle from Draft to Publish, then click Save.
For reference, your published automation should look like this:
Fire custom events for forms and calendars
Custom events let you fire an extra, named Facebook event for one specific form or calendar, matched by its name, so you can build an audience or optimize a campaign around that one source. By default your standard Lead and Schedule still fire and record alongside it, and you can optionally switch that off so only your custom event fires for that one matched form or calendar (see Also fire the standard event below). For example: a newsletter_lead event only for your newsletter form, or a strategy_call_booking event only for your strategy-call calendar.
Video guide
Open the custom events option and click Create
On the GoHighLevel install card in your dashboard, expand Click here if you want to fire custom events for your GoHighLevel forms and calendars, then click Create.
Fire a custom Facebook event for a specific form or calendar, matched by its name (the GoHighLevel "source"). Your standard Lead and Schedule still record in TrueMetriks. Created events appear under Facebook → Custom events, tagged GoHighLevel, where you can edit or remove them.
Fill in the Create custom event popup
Create custom event
Only fire when the GoHighLevel form or calendar source contains this text. Leave blank to fire on every Lead. Case-insensitive.
- Standard event. Pick Lead for a form or Schedule for a calendar. This is the most important choice: Lead is for forms, Schedule is for calendars.
- Custom event name. Type any name you want, for example
newsletter_lead. This is the name the event shows under in Facebook. Use letters, numbers and underscores. - Fire when. Leave it on Always, or choose New customers only / Returning customers only to fire just for first-time or repeat contacts. See New vs returning customers for what those mean.
- Source filter. This is where you tell TrueMetriks which form or calendar to match. Type the name of the form (for Lead) or the calendar (for Schedule). The match is case-insensitive and a "contains" match. Leave it blank to fire the custom event on every Lead (or every Schedule) instead of one specific source.
- Also fire the standard event. This checkbox only appears once you have typed a Source filter. Leave it on (the default) to fire and record both the standard event and your custom event, exactly as before. Turn it off to fire and record only your custom event for that matched source: the standard
LeadorSchedulewill not fire to Facebook and will not show up in your TrueMetriks reports. Use this when one specific form should count as its own conversion instead of a Lead, for example a registration form counted ascomplete_registration.
Where to find the form or calendar name
For a form, open Sites > Forms in GoHighLevel and edit the form. The name shown at the top is the source - copy it into the Source filter.
For a calendar, open Calendars in GoHighLevel, edit the calendar, and look at Calendar name under Basic details. Copy that into the Source filter.
Basic details
Paste the name into the Source filter exactly as it appears, then click Create.
What you get after clicking Create
The event appears under Facebook > Custom events in your dashboard, tagged GoHighLevel with the source it matches. From here you can edit when it fires or remove it.
Custom events
TrueMetriks also creates a matching Custom Conversion inside Facebook Ads Manager (Events Manager > your pixel > Custom conversions). It starts Inactive and turns Active once the first matching event lands. Give it 30 to 60 seconds before you test.
Test it
Submit the form (or book the calendar) you filtered on. In Facebook Events Manager > Test events you will see both the standard event and your custom event arrive together - Lead plus your custom name for a form, Schedule plus your custom name for a calendar.
Custom coded forms and calendars
If you built your own form or your own booking widget on a GoHighLevel page instead of using GoHighLevel's, TrueMetriks still tracks the visitor but it never learns that they submitted anything, so no Lead or Schedule conversion is recorded. One line of your own JavaScript fixes that.
When you need this
Only when the form or the booking widget is custom coded: your own HTML and JavaScript, or a third-party embed that is not a GoHighLevel form and not a GoHighLevel calendar.
A normal GoHighLevel form or calendar needs none of this. Those record their Lead and Schedule automatically, and adding the code below to one of them would count the same conversion twice. Only use it where you built the form or the booking widget yourself.
The symptom, if you are not sure: the person shows up in your dashboard with their email and phone and you can see their session, but no Lead (or no Schedule) ever appears for them.
Add the helper to your page
Paste this once into the funnel's Head tracking code, underneath your TrueMetriks snippet. A custom code block on the page works too, as long as it comes after the snippet.
<script>
function tmSendConversion(eventName, contact) {
if (window.__tmCaptureIdentity) window.__tmCaptureIdentity(contact);
if (window.tmFireEvent) window.tmFireEvent(eventName, {}, contact);
}
</script>
Optimize JavaScript must be off for any of this to work. With it on your snippet is lazy loaded, so it is not there yet when your form submits and the conversion is lost.
Fire Lead from your custom form
In your own success handler, after the submission has actually gone through:
// Form submitted successfully
if (!window.__tmLeadSent) {
window.__tmLeadSent = true;
tmSendConversion("Lead", {
email: "[email protected]",
phone: "+15551234567",
first_name: "Jane",
last_name: "Doe"
});
}
Replace the example values with the ones the person actually typed into your form.
Fire Schedule from your custom calendar
Exactly the same shape in your confirmed-booking handler, with the event name changed to Schedule:
if (!window.__tmScheduleSent) {
window.__tmScheduleSent = true;
tmSendConversion("Schedule", {
email: "[email protected]",
phone: "+15551234567",
first_name: "Jane",
last_name: "Doe"
});
}
What to put in the contact details
emailis the one that matters. It is what ties the conversion to the visitor's session and to the ad click that brought them in. Send the real address they typed. A merge tag that has not been filled in, such as{{contact.email}}arriving as literal text, is ignored.phoneshould carry the country code, for example+15551234567.first_nameandlast_nameare optional and can be left out.- Every field is optional in the sense that an event with no email still records, but it cannot be matched back to the person nearly as reliably, so send the email whenever you have it.
LeadandScheduleare the only two names to use here. They are the same conversions the dashboard and Facebook already understand, so they show up in your reports and fire to every connected ad platform exactly like a native GoHighLevel form would.
Two rules that stop double counting
1. Fire it once, and only when the submission actually succeeded. Put the call in your success path, after your own save or booking request comes back OK, never on the button click. A hand-fired conversion has no safety net here: two calls are two conversions in your reports and two conversions sent to your ad platforms. The if (!window.__tmLeadSent) guard in the examples is what stops an impatient double click sending it twice, so keep it in.
2. Do not also send the same form through a receiving webhook. If you have a GoHighLevel workflow POSTing this same submission to a TrueMetriks webhook URL, pick one or the other. Doing both records the conversion twice.
How to test
After installing the snippet (and the webhook, if you set one up), the next step is verifying everything is firing correctly. See Test your integration for the full walkthrough.
Something not firing? See Troubleshooting.
Frequently asked questions
Why must Optimize JavaScript be turned off?
Because it lazy loads your tracking snippet. GoHighLevel's own description is that custom javascript and html codes will be lazy loaded, and your snippet is custom JavaScript, so instead of running as the page opens it runs many seconds later. On a real customer funnel we measured 10,628ms with it on versus 401ms with it off. In that gap visitors are not tracked at all, a visitor can be split into two people so their landing page and thank-you page land in different sessions, and the ad click id can be missed so the sale is not credited to the ad. It is set per funnel, in Settings > Performance & compliance, so check every funnel you track.
Where do I find the tracking snippet?
In your TrueMetriks dashboard at Settings > Tracking > GoHighLevel. The snippet has a Copy button and is generated for your account.
Do I need to paste the snippet on every funnel?
Yes. GoHighLevel scopes the Head tracking code per funnel, so each funnel needs the snippet pasted into its own Funnel settings. The snippet itself is the same across all funnels in your account.
What is the Checkout URL pattern?
It is the word the tracker looks for in your URL to identify a checkout page. The default is checkout, which matches GHL checkout URLs. If your funnels use a different word, change it in your dashboard and click Save. The code does not change.
How do I fire a custom Facebook event for one specific form or calendar?
On the GoHighLevel install card, expand Click here if you want to fire custom events for your GoHighLevel forms and calendars and click Create. Pick Lead for a form or Schedule for a calendar, type a custom event name, and put the form or calendar name in the Source filter. The custom event then fires alongside the standard Lead or Schedule.
What is the Source filter and where do I find the name?
The Source filter is the name of the GoHighLevel form or calendar you want to match. For a form, open Sites > Forms and copy the form name. For a calendar, open Calendars > edit the calendar and copy the Calendar name under Basic details. The match is case-insensitive. Leave it blank to fire on every Lead or every Schedule.
Which standard event do I pick - Lead or Schedule?
Lead is for forms and Schedule is for calendars. Pick Lead to fire a custom event for a form submission, and Schedule to fire one for a calendar booking.
When do I need the webhook?
If you sell payment plans or subscriptions, the recurring charges happen after the customer leaves the page, so the script cannot see them. The webhook signals TrueMetriks every time a payment posts in GoHighLevel.
Do I need both the script AND the webhook?
For payment plans or subscriptions, yes. The script handles pageviews, leads, and the first purchase from the browser. The webhook handles every recurring payment after that.
Which Custom Data keys does the GoHighLevel webhook need?
Fill in Custom Data on every workflow, including one built on GoHighLevel's own Payment Received trigger, and use these exact keys: email, amount and product are required, currency is required unless your sales are in USD, order_id is recommended, and first_name, last_name and phone are optional. Map product to the merge field that holds the product name, not to a product or price id. Leave Headers empty.
My purchases show a long code instead of the product name. Why?
Because the workflow is not mapping product to the product name. GoHighLevel identifies the product by an internal id, so that code is what gets recorded and what your reports show, and a Facebook custom conversion that matches on the product name never fires. Open your workflow's Webhook action, add the product key under Custom Data, and map it to the merge field holding the product name. The full key list is in the Custom Data table on this page.
My form is custom coded and no leads are showing. Why?
A form you coded yourself does not tell TrueMetriks that it was submitted, so no Lead is recorded even though the visitor, their email and their session are all captured. Add the short snippet under Custom coded forms and calendars on this page and call it from your form's success handler.
Do I need this for normal GoHighLevel forms and calendars?
No. A GoHighLevel form records a Lead and a GoHighLevel calendar records a Schedule on their own, with nothing to add. The snippet is only for a form or a booking widget you built yourself, and adding it to a normal GoHighLevel form would count the same conversion twice.
Why does my purchase show a much bigger amount than the payment?
Almost always an extra money field in the webhook's Custom Data. If the field you mapped to amount arrives empty, TrueMetriks falls back to the next money-like key in the payload, so a revenue or cash collected key holding a running total gets recorded as the sale. Make sure amount always has a value and delete every other money key from Custom Data.