Google's bidder learns from your offline conversions more aggressively than any other platform in the fanout — which makes this the one wire-up where getting the gclid, the hash, and the timezone right pays back the fastest. Here is the request, the GAQL that proves it landed, and the six ways it silently fails.
This is the Google Ads half of the server-side conversion attribution stack — the OfflineConversionUploadGclidService request, the prerequisites that must be true first, the verification queries, and the failure-mode checklist. It assumes the pillar's prerequisites are in place: a Worker receiving the canonical event, a milestone definition in writing, and consent propagating through the pipeline.
Prerequisites — what must already be true
Five things, each checkable in the Ads UI before you write any wire-up code:
- A developer token at standard access (basic access is rate-limited too hard for production) — apply 1–3 business days ahead.
- An offline conversion action (Tools → Conversions → New → Import → CRM). Set count to "One" for service businesses, attribution to data-driven if eligible, and capture the resource name
customers/{id}/conversionActions/{id}— the request needs it. - OAuth credentials with the
adwordsscope; the refresh token lives in the Worker as a secret. - The gclid flowing to the CRM. Landing pages capture
?gclid=from the URL, persist it as a hidden form field, and store it on the record. No gclid, no attribution — the upload becomes noise. - Enhanced-conversion data hashed at the Worker — email and phone SHA-256 hashed (lowercased, trimmed), plaintext never appearing downstream of the CRM.
The request payload
One UploadClickConversionsRequest carrying an array of ClickConversion records:
{
"customer_id": "1234567890",
"conversions": [
{
"gclid": "Cj0KCQiA...",
"conversion_action": "customers/1234567890/conversionActions/987654321",
"conversion_date_time": "2026-05-26 14:32:18-05:00",
"conversion_value": 7500.00,
"currency_code": "USD",
"order_id": "crm-record-abc-123:lead-milestone",
"user_identifiers": [
{ "hashed_email": "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8" },
{ "hashed_phone_number": "5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5" }
]
}
],
"partial_failure": true,
"validate_only": false
}
Six fields warrant attention. gclid is the captured click ID — expired ones (older than the ~90-day window) are dropped silently and surface only in partial_failure_error. conversion_action must match the account exactly, or you get CONVERSION_ACTION_NOT_FOUND. conversion_date_time must be in the account's reporting timezone (YYYY-MM-DD HH:MM:SS±HH:MM) — a UTC Worker firing into a UTC-5 account dates conversions five hours early, sometimes outside the window. conversion_value / currency_code are the closed-revenue amount and ISO 4217 code in the account's reporting currency. order_id is the dedupe key — use a stable, globally-unique value the CRM owns. user_identifiers carries enhanced conversions, each a hashed_email, hashed_phone_number, or address block, hashed as hex SHA-256, lowercase.
Keep partial_failure: true so a single bad record doesn't reject the batch — Google returns per-conversion failures inline instead.
Enhanced-conversion hashing — where Google differs from Meta
Three normalization steps before the hash, and one place this diverges from Meta CAPI: email — lowercase, trim, SHA-256 (Google does not do Gmail dot/plus normalization as of 2026 — lowercase + trim only); phone — prefix with + and country code, strip non-digits, then hash, so (415) 555-0100 becomes +14155550100 (Meta omits the prefix — this is the divergence that breaks teams reusing one hash function); name — lowercase and trim. Output must be hex-encoded lowercase — Google rejects uppercase hex silently.
The GAQL query that confirms it landed
Uploads appear in the Conversions UI within 24 hours (faster for high-traffic accounts). This query confirms volume independently of the UI:
SELECT
segments.conversion_action,
segments.date,
metrics.all_conversions,
metrics.all_conversions_value
FROM customer
WHERE segments.date DURING LAST_7_DAYS
AND segments.conversion_action = 'customers/1234567890/conversionActions/987654321'
The all_conversions count should track your CRM milestone count within about ±5% (allowing for click-ID expiry, dedupe drops, and validation failures). A materially lower count is the symptom; the failure modes below are the diagnosis.
The six failure modes
- gclid expiry. The window is ~90 days. Capture the click date alongside the gclid; reject older events at the Worker before upload.
- Conversion-action mismatch. Pin the resource name in the Worker's env; a wrong customer-ID prefix is the cleanest error signature.
- Hash format. Uppercase hex, missing canonicalization, or base64 all fail matching silently — the conversion uploads but enhanced attribution doesn't happen. Unit-test against Google's test vectors.
- Timezone drift. Format
conversion_date_timein the account's reporting timezone, retrieved once at Worker start and cached. - Currency mismatch.
currency_codemust match the account; multi-currency operators convert at the Worker, not Google's edge (see multi-currency attribution). - Dedupe-key collision. Reusing an
order_idacross distinct conversions silently merges them. Use{record_id}:{milestone_name}, not just{record_id}.
Post-launch verification
Four checks before you call the Google side done: upload success rate in Logpush above ~98% (sub-95% means a failure mode is firing systematically); conversion-count parity via the GAQL query daily for 14 days, tracking the CRM count within ±5%; enhanced-conversion match rate in the Conversions UI's Diagnostics tab, which should climb past ~60% within 30 days (a low rate points at hash-format failures); and partial-failure log review — treat the inline per-conversion failures as warnings, not noise, and the systematic patterns surface in the first week.
Closing
The Google Ads offline-conversion upload is the single most consequential wire-up in the stack, because Google's smart-bidding learns from this signal harder than any other platform's. Getting it right inside the first month is the highest-leverage move you can make on the cost-per-acquired-customer curve. The failure modes above are not theoretical — every one shows up in wire-ups that shipped without oversight — and the verification checklist is non-negotiable. The boring path that prints.
Ready to wire Google Ads offline conversions correctly the first time? Book a 30-minute call →
Get the kit, not just the theory.
We'll send the build checklist behind this post — and the next pillar when it ships. One email, no drip sequence. Unsubscribe in one click.