Integration · Google Ads & GA4
Give Google back the conversions the browser lost
RoasProof captures the gclid on landing, keeps it through the whole customer journey, and reports the conversion straight to Google Ads with hashed identifiers attached, plus a matching GA4 event over the Measurement Protocol.
RoasProof reports conversions to Google Ads through click conversion uploads: the gclid (or wbraid/gbraid) captured on landing is stored in first-party storage, attached to the eventual order, and uploaded with hashed identifiers for Enhanced Conversions. The same conversion is also sent to GA4 via the Measurement Protocol, so ads and analytics agree.
A click conversion upload, as Google Ads receives it
Orders are reported through the Google Ads API as click conversions. That's the same mechanism Google recommends when the browser can't be trusted to fire a tag.
POST /v20/customers/{customer_id}:uploadClickConversions
{
"conversions": [{
"gclid": "Cj0KCQjwm5e5BhCCARIsA…",
"conversionAction": "customers/8613…/conversionActions/9145…",
"conversionDateTime": "2026-06-21 09:40:17+00:00",
"conversionValue": 184.50,
"currencyCode": "EUR",
"orderId": "84213",
"userIdentifiers": [
{ "hashedEmail": "9f3d0c52ab…" },
{ "hashedPhoneNumber": "1b6e21c84f…" }
],
"consent": {
"adUserData": "GRANTED",
"adPersonalization": "GRANTED"
}
}],
"partialFailure": true
}The gclid was saved a week earlier
Captured on the landing page, stored first-party against the visitor, and attached to the order at purchase, days or weeks after the URL parameter disappeared.
Timestamps land in the right column
conversionDateTime carries the actual order time with timezone offset, so conversions appear on the correct day in your reports instead of whenever an upload batch ran.
orderId keeps uploads idempotent
Retries and re-uploads of the same order carry the same orderId, so a delivery hiccup on our side can never become a duplicate conversion on yours.
Consent signals included
Consent status recorded at capture time is forwarded with each conversion, as required for uploads covering EEA traffic.
gclid, wbraid, gbraid: captured and persisted first-party
Google splits its click IDs across three parameters depending on platform and privacy state. We capture whichever one arrives and keep it for the whole conversion window.
- gclid
- Cj0KCQjwm5e5BhCCARIsA…
- wbraid
- -
- gbraid
- -
- utm_campaign
- brand-search-us
- landing
- /products/trail-jacket
- captured
- 2026-06-14 09:41:22 UTC
- attached_to
- order #84213
All three parameters, one record
gclid for standard web clicks, wbraid and gbraid for iOS traffic where Google withholds the full click ID. Whichever arrives is stored against the visitor identity.
Persistence beats cookie lifetimes
The click ID lives in first-party storage on your domain, tied to the visitor rather than to a cookie Safari deletes after seven days. A purchase three weeks later still uploads with its click attached.
Usable for the full click window
Click conversions can be reported well after the click. Because the ID never gets lost, late conversions (subscriptions, B2B deals, considered purchases) still land in Google Ads.
What exactly is a gclid and how long does it stay usable? See the gclid glossary entry, and how server-side tracking keeps it alive past browser limits.
Hashed identifiers, for when the click ID alone isn't enough
Enhanced Conversions let Google match conversions using hashed first-party data. We support both flavors, populated from the same identity graph that resolved the order.
Enhanced Conversions for web
Hashed email and phone ride along with conversions your tag already measures, recovering matches that third-party cookies used to provide. Your existing conversion actions keep working. They just match more often.
Enhanced Conversions for leads
For lead-gen funnels where the sale closes offline, Google matches the hashed email or phone from the original lead back to the ad click on its side, so you don't need a gclid at conversion time.
| identifier | normalization | where we get it |
|---|---|---|
| hashedEmail | Trimmed, lowercased, then SHA-256 hashed | Order or lead email from your store, form, or API event |
| hashedPhoneNumber | E.164 format, then SHA-256 hashed | Checkout or lead-form phone, country-normalized |
| address info (optional) | Hashed first/last name with plain city, region, and postal code | Billing or shipping address on the order, when you enable it |
Keep GA4 telling the same story as Google Ads
The same resolved conversion is also sent to GA4 server-side, so your analytics, your ad platform, and your bank account finally agree.
client_id recovered from the session
The _ga client ID is captured on-site and stored with the visitor, so the server-sent purchase attaches to the same GA4 user and session as their browsing did.
transaction_id prevents double counting
GA4 deduplicates purchases by transaction ID. The server event carries the order ID, so it merges cleanly with anything your gtag already reported.
Session attribution preserved
session_id is included with each event, so source/medium attribution stays intact and server conversions don't get dumped into (not set).
POST https://www.google-analytics.com/mp/collect
?measurement_id=G-8K2P4XQ&api_secret=•••
{
"client_id": "1191633108.1780828882",
"events": [{
"name": "purchase",
"params": {
"transaction_id": "84213",
"value": 184.50,
"currency": "EUR",
"session_id": "1781429900"
}
}]
}Uploads you can audit, row by row
Google Ads uploads are batched and partially failable: one bad row shouldn't sink the batch, and no failure should be invisible.
Continuous batched uploads
Conversions upload on a rolling schedule, well inside the click window. New orders typically reach Google Ads within minutes of the order webhook arriving.
Partial failures, surfaced
Uploads run with partialFailure enabled, and per-row errors are shown next to the exact conversion they affected: an unparseable gclid, a click older than the window, a mismapped conversion action.
Per-conversion status & replay
Every conversion shows its upload status, request, and response. Fix the cause (a wrong conversion action, a revoked OAuth grant) and replay failed rows with one click.
Stop letting Google guess which clicks converted.
Connect your Google Ads account, map a conversion action, and your next order uploads with its gclid attached, verified end to end before you go live.