Google Ads conversions
Google needs a click id (gclid, gbraid or wbraid) – or, with enhanced conversions for
leads, a hashed e-mail. 3x copies the click ids from the session. Two ways to deliver:
| CSV feed (recommended start) | Google Ads API | |
|---|---|---|
| Developer token | not needed | needed (Basic or Standard access) |
| Latency | Google pulls on its schedule (daily or every 6 h) | seconds |
| Status in 3x | feed | uploaded |
| Test mode | preview in Google Ads | validateOnly |
Both can run together – Google deduplicates on click id + action + time (the API sends the event id
as orderId).
Conversion actions
Google Ads → Goals → Conversions → Summary → New conversion action → Import → Other data sources or CRMs → Track conversions from clicks. Create one action per definition (category Submit lead form, Book appointment, Contact, …; count One). If browser-side gtag actions exist for the same events, make only one of them primary to avoid double counting in bidding.
Put the action's exact name into providers.google_ads.feedName (feed) and its id or resource
name customers/<id>/conversionActions/<id> (ctId in the action's URL) into
providers.google_ads.conversionAction (API).
Option A – scheduled CSV feed
PUT /v1/projects/{project}/destinations/google_ads
{
"enabled": true,
"mode": "live",
"transport": "feed",
"settings": {
"customerId": "1234567890",
"loginCustomerId": null,
"apiVersion": "v23",
"enhancedConversionsForLeads": false,
"feed": { "enabled": true, "username": "google-ads-feed", "timezone": "Europe/Berlin" }
},
"secrets": { "feedPassword": "<openssl rand -base64 32>" }
}Feed URL (HTTP Basic auth with username / feedPassword):
https://<api-host>/v1/projects/<project id>/feeds/google-ads.csvIt lists conversions of the last 60 days in the format Google expects:
Parameters:TimeZone=Europe/Berlin
Google Click ID,GBRAID,WBRAID,Conversion Name,Conversion Time,Conversion Value,Conversion Currency
Cj0KCQ…,,,Demo booked (Server),2026-10-14 15:02:11,500.00,EUR?definition=<key> or ?source=<source> narrows the feed – useful when Google's data manager wants
one connection per conversion action. With enhancedConversionsForLeads: true an Email column with
the normalized SHA-256 hash is added (only after accepting the customer data terms in Google Ads).
Then in Google Ads: Goals → Conversions → Uploads → Schedules → New schedule → source HTTPS →
the URL above → user name and password → daily (or every 6 h) → save, then Run now once and check
the upload diagnostics. Use a host that answers without redirect (curl -I it first): redirects
can drop the credentials.
Option B – Google Ads API
Settings as above with "transport": "direct" and these secrets:
| Secret | Where to get it |
|---|---|
developerToken | manager account → Tools → API Center |
clientId, clientSecret | Google Cloud console → enable Google Ads API → Credentials → OAuth client |
refreshToken | OAuth consent with scope https://www.googleapis.com/auth/adwords (e.g. OAuth Playground with your client) |
Set loginCustomerId to the MCC id when access goes through a manager account. 3x calls
customers/{customerId}:uploadClickConversions with partialFailure: true; in test mode with
validateOnly: true. Access tokens are cached until a minute before they expire.
With "transport": "composio" (and adConnectionId of your Google connection) the same call goes
through the Composio proxy using the connection's OAuth – no developer token or refresh token in 3x.
Limits and consent
- Click conversions older than 90 days are rejected (3x skips them after 89 days).
- No consent signal (
adUserData) is sent by default. In the EEA Google may restrict conversions without a consent signal; check the upload diagnostics. X3_GOOGLE_ADS_API_VERSION(defaultv23) andsettings.apiVersionpin the API version; Google sunsets versions roughly yearly.