GoHighLevel Offline Conversion Tracking: Send Closed Sales to Google Ads and Meta (2026)

GoHighLevel offline conversion tracking sending closed-won sales to Google Ads and the Meta Conversions API.

100% Human Written By MD Niamul
GoHighLevel Automation | Ai Agent Automation | Google Ads | Full‑Stack Web Analytics Conversion Tracking & Server Side Tracking Specialist

Quick Answer

GoHighLevel offline conversion tracking means sending your closed-won sales from the GoHighLevel (GHL) pipeline back to Google Ads and Meta, with the deal value. Save the visitor’s click IDs (GCLID, GBRAID, WBRAID and FBCLID) in contact custom fields when the lead comes in. Then build a workflow on Opportunity Status Changed to Won that fires a Custom Webhook into n8n, Make or Zapier, or into a server-side GTM container. From there, upload the sale to a Google Ads offline conversion action and send a Purchase event to the Meta Conversions API.

Key Takeaways

  • Leads are not sales: if Google Ads and Meta only see form fills, they keep finding people who fill forms, not people who pay.
  • The click ID is everything: without a GCLID on the contact, Google Ads can’t match a sale to an ad click. Meta matches better with FBCLID plus hashed email and phone.
  • GHL’s built-in Google Ads action has a gap: per HighLevel’s own help article, “Add to Google Ads” works with triggers like Form Submission and Order Purchase, not with a pipeline deal being marked Won.
  • One webhook can feed both platforms: a single Won workflow can send the sale to Google Ads and Meta at the same time.
  • Timing matters: Google Ads accepts clicks up to 90 days old for most import sources, and Meta wants most events within 7 days of the sale.
  • Revenue opens up better bidding: real deal values let you move to value-based bidding in Google Ads and Meta.

Introduction Why Your Ads Keep Finding the Wrong Leads

Here’s a pattern I see in a lot of GoHighLevel accounts. The agency is proud of its cost per lead. The sales team is quietly frustrated, because half those leads never pick up the phone.

Nobody is lying. Google Ads and Meta are doing exactly what they were told to do: get more form submissions. They have no idea which of those people later paid $5,000 and which ones ghosted the sales rep.

The fix is to send the sale back. When a deal moves to Won in your GHL pipeline, the ad platforms should hear about it, with the value. After a few weeks of that, they start looking for buyers instead of form fillers.

In this guide I’ll show you the setup I use for GoHighLevel clients: how to capture the click IDs, how to trigger on closed-won deals, and how to send them to Google Ads and Meta through n8n, Make, Zapier or server-side tracking.

If you only need Google Ads lead conversions from GHL forms, start with my earlier guide on Google Ads offline conversion tracking with GoHighLevel CRM. This post is about closed-won revenue, for both Google Ads and Meta.

Why Track Closed-Won Sales Instead of Leads?

Tracking only leads Tracking closed-won sales
What the ad platform learns Who fills in forms Who actually buys
Value per conversion Same for every lead, or none The real deal value from the opportunity
Bidding you can use Maximize conversions, target CPA Maximize conversion value, target ROAS
Typical result Cheap leads, low close rate Fewer junk leads, more revenue per dollar
Comparison of tracking GoHighLevel leads only versus sending closed-won sales with value to Google Ads and Meta.

Prerequisites & Setup Checklist

  • GoHighLevel: admin access to the sub-account, a pipeline with a clear Won status, and opportunity values filled in.
  • Google Ads: auto-tagging switched on, and permission to create conversion actions.
  • Meta: a pixel (dataset) in Events Manager and a Conversions API access token.
  • Automation tool: n8n, Make or Zapier, or a server-side GTM container on Stape.
  • Consent: if you have leads from the EEA or UK, make sure your forms and privacy policy cover sending conversion data to ad platforms.

Video Tutorial Walkthrough

In the video I explain the lead vs. closed-won problem, show where the click IDs go in GoHighLevel, set up the pipeline trigger, and talk through the Zapier, Make and n8n options.

Step-by-Step Implementation Guide

I split the setup into eight phases. The first three are inside GoHighLevel and Google Ads. Then we move the data.

Phase 1: Create the Offline Conversion Action in Google Ads

  1. In Google Ads, click + and choose Conversion action.
  2. Pick Conversions offline as the source. You can skip the data source for now.
  3. Choose the Purchase category and name it something you will copy exactly later, such as GHL - Closed Won.
  4. Choose Use different values for each conversion, so every deal sends its own value.
  5. Set the click-through window to 90 days. For a one-off service sale, set Count to One.

Keep the name in a note. If the name you send later is off by one space or capital letter, Google Ads rejects the row.

Phase 2: Save Click IDs on the GoHighLevel Contact

  1. In GHL, go to Settings → Custom Fields and create text fields for GCLID, GBRAID, WBRAID and FBCLID.
  2. Add them to your forms as hidden fields, and set each field’s query key to the URL parameter (gclid, gbraid, wbraid, fbclid).
  3. Submit a test lead from a URL like ?gclid=TEST123&fbclid=TEST456 and check the contact record.

Here’s the catch I run into most: hidden fields only read the URL of the page the form is on. In a multi-step funnel, or when the visitor browses before converting, the click ID is gone by the time they submit. In those cases I store the IDs in a first-party cookie on the landing page and pass them into the form. That’s the same idea I use for Pipedrive click ID capture.

GoHighLevel contact custom fields for GCLID, GBRAID, WBRAID and FBCLID filled from hidden form fields.

Phase 3: Make Sure the Opportunity Has a Real Value

  1. Open your pipeline and confirm the opportunity value is filled in when a deal closes.
  2. Agree with sales on one rule: a deal is only marked Won when the client has paid or signed.
  3. If you sell in several currencies, keep the currency on the contact or opportunity as a custom field.

Garbage in, garbage out. If reps mark deals Won early, you will teach Google Ads and Meta the wrong thing.

Phase 4: Build the Closed-Won Workflow in GoHighLevel

  1. Go to Automation → Workflows and create a new workflow.
  2. Add the trigger Opportunity Status Changed, filtered to your pipeline, with the status changed to Won.
  3. Add the action Custom Webhook, method POST, and paste the webhook URL from n8n, Make or Zapier.
  4. Send the fields below as JSON. Pick each value from the merge field menu, so the keys stay the same every time.
Key in the webhook Value from GoHighLevel
contact_id, opportunity_id Contact ID and opportunity ID
email, phone, first_name, last_name Contact fields
deal_value, currency Opportunity value and your currency field
gclid, gbraid, wbraid, fbclid The custom fields from Phase 2
won_at, lead_created_at The date the deal was won and the date the contact was created

Why not GHL’s built-in “Add to Google Ads” action? HighLevel’s help article lists the triggers it supports, and pipeline status changes aren’t among them. A Custom Webhook works on any trigger, and the same request can feed Meta too.

GoHighLevel workflow with the Opportunity Status Changed to Won trigger and a Custom Webhook action.

Phase 5: Turn the Webhook into Google Ads and Meta Payloads

In n8n, add a Webhook node to receive the GHL request, then a Code node with the script below. It hashes the customer data for Meta, builds the fbc value from the stored FBCLID, formats the time the way Google Ads wants it, and skips Google when there’s no click ID.

n8n Code node: GoHighLevel closed-won to Google Ads and Meta
// GoHighLevel closed-won payload builder by MD Niamul (mdniamul.com)
// n8n Code node, mode "Run Once for Each Item", placed after the Webhook node
// that receives GoHighLevel's Custom Webhook when an opportunity is Won.
// Self-hosted n8n: allow crypto with NODE_FUNCTION_ALLOW_BUILTIN=crypto
const crypto = require('crypto');
const b = $json.body || $json;
const CONVERSION_NAME = 'GHL - Closed Won';   // must match Google Ads exactly
const ACTION_SOURCE = 'system_generated';     // use 'phone_call' if reps close by phone

const clean = v => (v === undefined || v === null) ? '' : String(v).trim();
const sha = v => clean(v) ? crypto.createHash('sha256').update(clean(v).toLowerCase()).digest('hex') : undefined;
const digits = clean(b.phone).replace(/[^0-9]/g, '');
const value = Number(String(b.deal_value || '0').replace(/[^0-9.]/g, '')) || 0;
const currency = (clean(b.currency) || 'USD').toUpperCase();
const wonAt = b.won_at ? new Date(b.won_at) : new Date();

// Google Ads offline conversion (needs one click ID)
const hasClickId = clean(b.gclid) || clean(b.gbraid) || clean(b.wbraid);
const googleAds = hasClickId ? {
  gclid: clean(b.gclid), gbraid: clean(b.gbraid), wbraid: clean(b.wbraid),
  conversion_name: CONVERSION_NAME,
  conversion_time: wonAt.toISOString().slice(0, 19).replace('T', ' ') + '+00:00',
  conversion_value: value,
  currency: currency
} : null;

// Meta Conversions API event (hashed customer data, fbc from the stored fbclid)
const leadTime = b.lead_created_at ? new Date(b.lead_created_at).getTime() : wonAt.getTime();
const fbc = clean(b.fbc) || (clean(b.fbclid) ? 'fb.1.' + leadTime + '.' + clean(b.fbclid) : undefined);
const userData = {
  em: sha(b.email) ? [sha(b.email)] : undefined,
  ph: digits ? [sha(digits)] : undefined,
  fn: sha(b.first_name) ? [sha(b.first_name)] : undefined,
  ln: sha(b.last_name) ? [sha(b.last_name)] : undefined,
  external_id: sha(b.contact_id) ? [sha(b.contact_id)] : undefined,
  fbc: fbc
};
Object.keys(userData).forEach(k => userData[k] === undefined && delete userData[k]);

const meta = {
  data: [{
    event_name: 'Purchase',
    event_time: Math.floor(wonAt.getTime() / 1000),
    event_id: 'ghl_won_' + clean(b.opportunity_id),
    action_source: ACTION_SOURCE,
    user_data: userData,
    custom_data: { value: value, currency: currency, lead_event_source: 'GoHighLevel' }
  }]
};

return { json: { google_ads: googleAds, meta: meta, skipped_google: !hasClickId } };

I tested it with sample GHL data. A messy value like $4,800.00 comes out as 4800. The email and phone hashes match SHA-256 of the normalized values, and a contact with no click ID returns google_ads: null instead of a broken row. Set CONVERSION_NAME to the exact name from Phase 1.

Using Make or Zapier instead? Same idea: map the webhook fields, hash email and phone for Meta, and use the built-in Google Ads offline conversion module.

Phase 6: Send the Sale to Google Ads and Meta

  1. Google Ads: send the google_ads object to your Google Ads offline conversion step (the Google Ads app in Zapier or Make, the Google Ads API, or a Google Sheet that Google Ads imports on a schedule).
  2. Meta: add an HTTP Request node that POSTs the meta object to https://graph.facebook.com/<version>/<PIXEL_ID>/events with your access token.
  3. While testing, add your test_event_code from Events Manager so the event shows in the Test events tab.

Want to check the Meta payload by hand first? Paste it into my free Meta CAPI payload builder. For the Google side, the free offline conversion builder checks the columns.

n8n workflow receiving a GoHighLevel webhook and sending the closed sale to Google Ads and the Meta Conversions API.

Phase 7: Optional Server-Side Route with Stape

If you already run server-side GTM, you can point the GHL webhook at your server container instead of n8n. A Data Client receives the request, and tags in the container send the Meta Conversions API event and the Google Ads offline conversion. I like this when a client already has their whole tracking in one sGTM container, because everything stays in one place.

I host these containers on Stape, or Stape EU for EU data. More on that in my server-side tracking service.

Phase 8: Switch to Value-Based Bidding

  1. Let closed-won conversions build up for a few weeks and check that values look right.
  2. In Google Ads, set the closed-won action as Primary and consider moving the lead action to Secondary.
  3. Move campaigns to Maximize conversion value, and later to a target ROAS once you have steady volume.
  4. In Meta, use the Purchase event with value for optimization and reporting.

Don’t flip everything on day one. With only a handful of sales a month, keep optimizing on leads and let the revenue data guide budget decisions first.

Google Ads conversion action GHL Closed Won showing imported GoHighLevel sales with values for value bidding.

GoHighLevel Offline Conversion Tracking: The Full Data Flow

# What happens Where
1 Visitor clicks an ad and lands with gclid or fbclid Your funnel
2 The form saves the click IDs on the contact GoHighLevel
3 Sales works the opportunity and marks it Won with a value GHL pipeline
4 The workflow sends a Custom Webhook GHL workflow
5 The Code node builds both payloads n8n, Make, Zapier or sGTM
6 Google Ads imports the sale; Meta receives a Purchase Google Ads, Meta Events Manager

Testing & Validation Workflow

  1. Contact test. A test lead from a tagged URL has GCLID and FBCLID in its custom fields.
  2. Workflow test. Move a test opportunity to Won and check the workflow’s execution log.
  3. Webhook test. n8n receives the request with every key filled.
  4. Meta test. The Purchase event appears in Test events with value and good match quality.
  5. Google Ads test. The upload shows in the conversion action’s diagnostics without errors. Fake test GCLIDs will fail, which is expected; use a real click for the final check.
  6. Monthly check. Compare Won deals in GHL with imported conversions in Google Ads and Meta.

Troubleshooting Common Mistakes

Mistake 1: The GCLID field is empty

The Problem: deals reach Won, but Google Ads gets nothing.

The Cause: the form wasn’t on the landing page, a redirect stripped the URL, or the form is an iframe embed on another site.

The Solution: store the click IDs in a first-party cookie on landing and pass them into the hidden fields.

Mistake 2: Google Ads rejects the conversion name

The Problem: uploads fail with an unknown conversion action.

The Cause: the name in the payload doesn’t match Google Ads character for character.

The Solution: copy the name straight from Google Ads into CONVERSION_NAME.

Mistake 3: Meta shows the event but low match quality

The Problem: Purchases arrive in Events Manager, but attribution is weak.

The Cause: only email is sent, or phone numbers include spaces and symbols before hashing.

The Solution: send email, phone (digits only, with country code), names, external ID and fbc, all normalized before hashing, as the script does.

Mistake 4: Old deals are ignored

The Problem: deals that took months to close never show up.

The Cause: the click is older than Google’s import window, or the Meta event time is more than 7 days in the past.

The Solution: send the sale as soon as it’s won, and for long sales cycles also send an earlier stage, like a qualified lead.

Mistake 5: Sales are counted twice

The Problem: Meta reports more purchases than you closed.

The Cause: GHL’s own Facebook Conversions API action and your webhook both send Purchase for the same deal.

The Solution: pick one sender, and always send a stable event_id like ghl_won_<opportunity id>.

What to Set Up Next

Want this built and documented for your account? Take a look at my GoHighLevel conversion tracking specialist page and my offline conversion tracking services.

Conclusion

The form fill is where your ads stop seeing the customer. Everything that happens after that, the calls, the follow-ups, the signed deal, lives in GoHighLevel.

So start small. Get the click IDs onto every new contact this week. Then build the Won workflow and send one test deal through to Google Ads and Meta. Once real sales are flowing back, you can decide when to let the ad platforms bid on revenue.

Summary by MD Niamul

GoHighLevel offline conversion tracking sends closed-won sales from the GoHighLevel (GHL) pipeline back to Google Ads and the Meta Conversions API, so both platforms optimize for revenue instead of raw leads. Here’s how I set it up. I save GCLID, GBRAID, WBRAID and FBCLID in contact custom fields through hidden form fields. Then I create a Google Ads offline conversion action with different values per conversion. A GHL workflow triggered by Opportunity Status Changed to Won sends a Custom Webhook to n8n, Make, Zapier or a server-side GTM container on Stape. That step uploads the sale to Google Ads and posts a hashed Purchase event to Meta. The catch: GHL’s built-in Add to Google Ads action doesn’t run on pipeline status changes, and old clicks or late events get rejected, so send each sale as soon as it’s won.

Frequently Asked Questions (FAQ)

1. What is GoHighLevel offline conversion tracking?

It’s the process of sending events that happen after the form fill, like a closed-won deal in your GoHighLevel pipeline, back to your ad platforms. Google Ads gets the sale with the GCLID and value. Meta gets a Purchase event through the Conversions API. That way the ad platforms learn which clicks turned into paying clients.

2. How do I connect Google Ads to GoHighLevel?

In GoHighLevel, connect your Google account under Settings, Integrations, Google, and pick the right Google Ads account. In Google Ads, create a conversion action with Conversions offline as the source. GHL’s Add to Google Ads workflow action can then send conversions for supported triggers like form submissions, provided the contact has a GCLID, GBRAID or WBRAID.

3. Why can’t I use Add to Google Ads for closed-won deals?

HighLevel’s help article lists the triggers the Add to Google Ads action supports, such as Form Submission and Order Purchase. A pipeline deal moving to Won isn’t one of them. So for closed-won revenue I use the Opportunity Status Changed trigger with a Custom Webhook and send the sale to Google Ads through n8n, Make, Zapier or server-side GTM.

4. What is the Meta Conversions API?

The Meta Conversions API is a server-to-server way to send events to Meta instead of relying only on the browser pixel. For GoHighLevel sales, it lets you send a Purchase event with the deal value, hashed email and phone, and the click ID, even though the sale happened in your CRM and not on your website.

5. Does GoHighLevel have a built-in Meta Conversions API action?

Yes, GoHighLevel has a Facebook Conversions API workflow action that sends events like Lead and Purchase from a connected pixel. You can use it, but don’t also send the same Purchase from a webhook, or Meta will count the sale twice. I prefer one webhook that feeds both Google Ads and Meta, so the data stays in one place.

6. Which click IDs should I store in GoHighLevel?

Store GCLID, GBRAID and WBRAID for Google Ads, and FBCLID for Meta, each in its own contact custom field. GBRAID and WBRAID show up on some iOS and app traffic instead of GCLID. Also make sure email and phone are captured, because Meta uses them, hashed, to match the sale to a person.

7. How fast do I need to send a closed sale?

As soon as it’s won. Google Ads imports clicks up to 90 days old for most file-based sources, and Meta expects most Conversions API events within 7 days of when they happened. If your sales cycle is longer than about three months, also send an earlier stage, like a qualified lead, so the platforms still learn.

8. Should I use n8n, Make or Zapier?

All three work. Zapier is the quickest to set up and has a ready Google Ads offline conversion step. Make gives more control at a lower price. n8n is my pick for clients who want to self-host and keep full control, and the Code node in this guide is written for it. Server-side GTM on Stape is another good route.

9. When should I switch to value-based bidding?

Once closed-won conversions with values are flowing in steadily, usually after a few weeks. In Google Ads, make the closed-won action Primary, then try Maximize conversion value before setting a target ROAS. With only a few sales a month, keep bidding on leads for now and use the revenue data for budget decisions.

10. Is it legal to send customer data to Google Ads and Meta?

It depends on where your leads are and what they agreed to. Email and phone must be hashed before sending, and leads in the EEA or UK need a clear consent basis and a privacy policy that covers ad platforms. I set up the technical side correctly, but check the legal side with your own adviser. This isn’t legal advice.

🚀 Need Help Advanced GHL Automation & Tracking Setup?

💬 Want this implemented without mistakes?

I’ve helped 850+ advertiser agencies & D2C brands unlock $11.6M+ revenue by implementing 1,500+ Ai Driven GoHighLevel full Business Automation+ client-side & server-side tracking systems.

⮏ My core services include:

⨭ GoHighLevel Automation — build CRM pipelines, WhatsApp automation, AI voice workflows & automated follow-ups end-to-end.
⨭ AI Agent Automation — deploy AI chatbots & agents for 24/7 lead qualification and support.
⨭ Paid Ads Management — Google Ads, Meta Ads.
⨭ Full Stack SEO/GEO/AEO Manager.
⨭ CRO-Focused Web Development — Landing Pages on WordPress, Shopify, Wix, Squarespace.
⨭ Google Tag Manager (GTM) — manage data layers & (Marketing platform Tag, Trigger & Variables).
⨭ Custom Code by GTM — HTML, CSS & JavaScript for the help of marketing platform advanced tracking.
⨭ Server-Side Tracking — Bypassing ITP/Ad-blockers for 99% accuracy (Stape Partner)
⨭ Google Analytics 4 (GA4) — visualize customer journeys.
⨭ Multi-Channel CAPI — Facebook, TikTok, Pinterest & Snapchat Conversion API & Klaviyo email marketing tools.
⨭ Third Party Checkout Conversion Tracking — Shopify, Stripe, GoQuick, ShipRocket, PayPal or more.
⨭ Google Consent Mode (GCM) — maintain GDPR compliance.
⨭ Marketing Automation — streamline workflows (Zapier/n8n/Make).
⨭ CRM Conversion Tracking — link offline sales to ads.
⨭ Tag Management — GTM & Third-Party Checkout Tracking.
⨭ Offline & CRM Tracking — HubSpot, Salesforce, Zoho, Pipedrive, Odoo, Webhook & Sheet.
⨭ Advanced Analytics — GA4, Google Looker Studio & Big Query (SQL) for deep data visualization.

If you want your GoHighLevel Business Automation & server side tracking done right the first time, message me.

Leave a Reply

Your email address will not be published. Required fields are marked *

Conversion tracking specialist dashboard showing Google Ads and GA4 data analysis with GTM integration.

MD NIAMUL

GoHighLevel Automation | Ai Agent Automation | Server Side Tracking Specialist

Niamul

If You Need GTM Listener Code Submit Your Email