LAB LIVE
SCANNING409 CAMPAIGNS
ANOMALIES15 FLAGGED
AVG LIFT+4.45× ROAS
CRM & Analytics

Bitrix24 → GA4 → Google Ads Offline Conversions — the End-to-End Loop Nobody Documents

Most Bitrix24 accounts capture a lead perfectly and then lose all attribution the moment a deal moves through the sales pipeline. The form submit is tracked as a Google Ads conversion; the qualified-lead stage two weeks later isn't; the won deal three months later definitely isn't. Smart Bidding keeps optimising for form fills instead of revenue. Here's the full loop — gclid capture, deal-stage triggers, GA4 Measurement Protocol, and Google Ads offline uploads — that fixes it.

Alex Sterling··11 min read

Bitrix24 is a solid CRM for the mid-market — Russian/CIS lead-gen agencies and service businesses run their entire pipeline through it. The default Google Ads integration captures a lead from a web form and inserts it as a Bitrix Lead entity. What almost nobody sets up is the return path: when the Lead becomes a Deal, when the Deal moves to Qualified, when the Deal is Won — none of those events flow back to Google Ads or GA4 by default. The advertising platform sees only the top of the funnel and optimises accordingly.

For any service business with a sales cycle longer than a day, this is a much bigger attribution gap than Consent Mode or iOS 14.5. If your average customer is worth 3 000 € and only 8% of form-fills become customers, the “450 ₽ lead” Google Ads reports has an effective per-customer cost of 70 000 ₽ that neither your ad account nor your CRM connects to the right campaigns. Smart Bidding is guessing.

The Loop, End to End

StageWhat must happenWhere it usually breaks
1. Ad clickgclid lands in URL, captured to cookie/localStorageMissing capture script or SPA that strips the query string
2. Form submitgclid + _ga stored as hidden fields on the formMarketer added form via Bitrix builder without hidden fields
3. Lead created in B24Custom fields UF_GCLID, UF_GA_CID populatedFields don't exist or aren't mapped in Lead form
4. Lead → DealCustom fields copied to Deal entity via workflowDefault Bitrix migration drops most custom fields
5. Deal stage change (Qualified / Won)Webhook triggers Google Ads OCI + GA4 MP uploadNo webhook or no upload script listening
6. Google Ads offline conversionUpload with gclid + conversion action + valueConversion action not created; upload fails silently
7. GA4 Measurement ProtocolServer event with client_id and CRM stageSession source lost — see the MP trap piece

Each stage is a small piece of glue. Together they close the loop. Miss any one and the whole chain fails silently — the deal Won stage happens, nothing goes back to Google Ads, and your bidding stays blind.

Stage 1–2 — Capturing gclid at Form Submit

The gclid arrives as a query parameter on the landing page. On any SPA or multi-step funnel, that gclid disappears the moment the user navigates. The reliable pattern:

Snippet patternOn every page load, read gclid, gbraid, wbraid, and _gcl_aw from the URL and document.cookie. Store all four in localStorage with a 90-day expiry timestamp. On form submit, hydrate hidden fields from localStorage. Do not rely on the Bitrix form builder's “auto-capture UTM” feature — it only reads the current URL, not stored values.

Alongside the click IDs, capture GA4's _ga cookie value and pass it as a hidden ga_client_id. This is the cross-key that lets you send GA4 Measurement Protocol events later with the same client identity as the browser session, keeping the user unified across CRM stages.

For Bitrix24's native form widget, add the hidden fields in the form designer under “Additional fields — Hidden.” Bind them to the CRM custom fields (see next section). For custom forms, POST directly to the crm.lead.add REST endpoint with the hidden field values in the payload.

Stage 3 — Custom Fields in Bitrix24

You'll need these custom fields, at minimum. Create them once on both Lead and Deal entities (Bitrix has separate custom-field namespaces per entity type — this is the mistake that eats most implementations).

  • UF_GCLID — string, max 255
  • UF_GBRAID — string, for iOS traffic
  • UF_WBRAID — string, for web-to-app iOS
  • UF_GCL_AW — string, Google Ads cookie
  • UF_GA_CID — string, GA4 client ID
  • UF_UTM_SOURCE, UF_UTM_MEDIUM, UF_UTM_CAMPAIGN — as strings
  • UF_FIRST_TOUCH_TS — datetime, first form submit

Bitrix's default Lead→Deal migration only copies the standard fields. Custom fields need an explicit workflow rule: “When Lead moves to Deal, copy UF_* fields from Lead to Deal.” Set this up under Automation → Workflows → Lead entity → conversion trigger.

Real audit exampleA Russian B2B services company running Bitrix24 with a beautiful Google Ads integration. Every Lead showed UF_GCLID populated. Every Deal had the field empty. Reason: workflow never wrote it during conversion. 100% of deals — 30–50 per month at 200 000 ₽ average value — were running without click-ID attribution. Google Ads Smart Bidding had zero signal beyond form-fills for 18 months. Adding one workflow rule (2 hours of setup) plus wiring the deal-stage webhook (6 hours) recovered per-campaign ROAS visibility. Best-performing campaign turned out to be a small remarketing group — historically capped at 200 ₽/day — that had been generating a third of all won revenue.

Stage 5 — Deal-Stage Webhooks

Bitrix24 supports outgoing webhooks per entity event. The two you need are:

01
Deal.stage.change

Fires on every stage transition. Filter server-side to the stages you care about — usually “Qualified” (SQL — high-value soft signal for Smart Bidding) and “Won” (primary conversion). The payload includes the deal ID; your handler fetches the full deal via crm.deal.get to read the custom fields.

02
Deal.update (fallback)

Some Bitrix deployments have stage.change disabled or delayed by up to 24 hours. Deal.update fires reliably on any edit; check the stage inside your handler and dedupe on deal_id + stage_id to avoid double-uploads if a sales rep re-saves the record.

Register the webhook via Bitrix REST: event.bind with event=OnCrmDealUpdate and your handler URL. The handler should be a small server (Cloud Run, VPS, Vercel Function) that receives the payload, validates it against a shared secret, and enqueues the upload to Google Ads and GA4.

Stage 6 — Google Ads Offline Conversion Import (OCI)

For each stage you care about, create a Google Ads conversion action of type “Import → Other data sources or CRM → Import from clicks (ClickConversion).” Two actions minimum:

  • Deal Qualified: secondary conversion, count = one per, include_in_conversions_metric = false, value = 0 (or lifetime-adjusted weighting)
  • Deal Won: primary conversion, count = one per, include_in_conversions_metric = true, value = actual deal amount

Upload via the Google Ads API UploadClickConversions endpoint. The payload keys on gclid (or gbraid/wbraid for iOS) and requires a conversion timestamp within the click's look-back window (default 90 days). Set conversion_value to the actual deal value and currency_code to the account currency.

Timing ruleUpload as soon as the stage changes — do not batch daily. Smart Bidding digests offline conversions within 6–24 hours; delaying uploads by a day means your bidding is a day behind the CRM. On multi-touch attribution windows > 30 days, that lag compounds.

Stage 7 — GA4 Measurement Protocol

Google Ads OCI covers the ad-side attribution but not the analytics side. For GA4 to see the same deal stages (and for you to be able to segment users in Explore reports by SQL/Won status), fire GA4 Measurement Protocol events from the same webhook handler.

Use the client_id stored in UF_GA_CID. The event should have a specific name (deal_qualified, deal_won) and include the deal value as value. Watch the classic MP trap — the session source will be missing unless you handle it. See the MP session-source piece for the Custom Channel Group workaround.

Do not use MP for the primary Ads conversion — GA4 Ads-linked conversion actions won't import MP-triggered events reliably. Keep OCI as the authoritative bid signal; MP as the reporting mirror.

Common Failure Modes

01
Custom fields present on Lead, empty on Deal

The single most common failure. Lead→Deal workflow never copies the UF fields. Fix: workflow rule with explicit field copies.

02
gclid captured but never persisted across sessions

User clicks the ad Monday, submits the form Wednesday. Without localStorage persistence, the Wednesday session has no gclid. Fix: 90-day localStorage store, hydrate on form load.

03
OCI uploaded but rejected silently as “click too old”

Google Ads OCI rejects clicks older than the account's look-back window (default 90 days, extendable to 540). B2B sales cycles > 3 months routinely blow past this. Extend the click look-back window in the conversion action settings before you start uploading.

04
Deduplication with online conversion tracking

If the form submit already fires an online Google Ads conversion, the OCI upload on stage change will create a second conversion for the same deal. Fix: give them distinct conversion actions with distinct goal categories, so bidding uses each without double-counting.

What Success Looks Like

MetricBefore OCI loopAfter OCI loop
Google Ads conversion signalForm submits onlyForm + SQL (secondary) + Won (primary)
Smart Bidding optimises forLead volume, indifferent to qualityRevenue-weighted qualified leads
Per-campaign ROAS visibilityNone — every campaign looks similarFull — bad campaigns visible within 60 days
Budget reallocation confidenceGut-based, monthlyData-driven, weekly

We measure the loop's value not in a single number but in the decisions it makes possible. On a typical B2B services account, closing the loop reveals that 60–80% of new revenue is driven by 20–30% of the campaigns. Without the loop, every campaign gets equal budget consideration; with it, the actual winners get scaled and the losers get paused within one quarter.

Audit Checklist

gclid captured to localStorage on page load, hydrated at form submit?Open a landing URL with ?gclid=test123, navigate away, come back with no query param, submit form — hidden field should still contain test123.
UF_GCLID present on Lead form after submission?Bitrix24 → CRM → last lead → custom fields section. Should contain the gclid string.
UF_GCLID copied to Deal on conversion?Convert the lead to a deal; open the deal; UF_GCLID must be present. If empty, workflow rule missing.
Webhook registered on OnCrmDealUpdate?Bitrix REST event.get. Should list your handler URL bound to the event.
Google Ads conversion actions created (Qualified + Won)?Google Ads → Goals → Conversions. Should show both actions with source = Import.
Click look-back window extended for long B2B cycles?Conversion action settings → Click through conversion window. Default 90 days is too short for many B2B; extend to 540 if cycles exceed.
GA4 MP event visible in DebugView with correct client_id?GA4 → Admin → DebugView. Trigger a fake stage change; deal_won event should appear with the browser's client_id.
Smart Bidding switched to include the Won conversion as primary?Campaign settings → Conversion goals. If “Deal Won” is set to secondary, bidding still optimises against form fills.

Why Now

Google Ads has been quietly deprecating anything that looks like inference-based conversion tracking in favour of explicit signals — data-driven attribution needs deal-stage data to differentiate signal weight, Smart Bidding relies on primary/secondary flags (see the Smart Bidding lever piece) that only mean something when the underlying data is CRM-truth. A Bitrix24 account without OCI wired to Google Ads in 2026 is running blind in a way it wasn't in 2022.

The loop is 20–40 hours of setup depending on your existing state. On any services or B2B account with a sales cycle longer than a week, it's the highest-ROI piece of tracking work available. See how we approach it inside conversion tracking audits.

Alex Sterling

Alex Sterling

Founder at Sterling Lab · CRM ↔ ad-platform attribution architect