Google Ads Conversions
Datafly Signal delivers offline and enhanced conversions to Google Ads server-to-server via the Google Ads API (ConversionUploadService). This replaces client-side conversion tags with direct server uploads, improving measurement accuracy and enabling conversion-based bidding optimisation without relying on browser-side scripts.
Delivery status. The Google Ads delivery currently targets Google Ads API v17, which Google has sunset, so uploads are rejected until the delivery moves to a supported version (#1127). Since 15 June 2026 Google also refuses uploadClickConversions from projects new to offline conversion import; new adopters should use the Google Data Manager integration. The blueprint below is correct for the upload format.
Why route Google Ads through Datafly Signal
- Conversions survive ad blockers and tracking prevention. A browser tag served from an advertising domain is blocked outright for part of your traffic, and Safari’s ITP shortens the cookies it can set. Signal sends the event from your own server, so the conversions Google Ads optimises against are not filtered by the browser.
- Email, phone number, first name and last name are hashed before leaving your infrastructure. Signal normalises (trim and lowercase) and applies SHA-256 inside your own deployment, so Google Ads receives match keys rather than raw personal data.
- Click identifiers are captured and replayed. Signal stores
gbraid,gclid,wbraidfirst-party at collection time and attaches them to the conversion later, so attribution survives even when the original landing cookie has expired. - Delivery is batched, rate-limited, and retried. Signal buffers to Kafka, respects the Google Ads rate limit, and retries on 429 or transient failure rather than dropping the event.
- Token lifecycle is handled for you. Signal obtains and refreshes the OAuth credentials Google Ads requires, so nothing expires quietly in the middle of a campaign.
- Presets for Retail, B2B / SaaS and Automotive. The mapping starts from a working configuration for your vertical instead of a blank field map.
- One consent decision governs every destination. Google Ads is gated by the same consent state as the rest of your stack, so a withdrawal applies everywhere at once.
- No vendor tag on the page. Removing it removes its page weight and its independent access to your visitors.
Prerequisites
Setting up Google Ads for server-side conversion tracking requires a Google Ads account, a conversion action, OAuth2 credentials, and (optionally) Enhanced Conversions enabled. Follow each step below before configuring the integration in Signal.
Google Ads Account
You need an active Google Ads account. If you manage multiple accounts through a Manager Account (MCC), you can use either the MCC customer ID or the individual account’s customer ID.
- Sign in to Google Ads.
- Your Customer ID is displayed in the top-right corner of the dashboard, formatted as
123-456-7890. - Note this value — you will need it when configuring the integration in Signal.
When entering the Customer ID in Signal, you can use either the dashed format (123-456-7890) or the plain numeric format (1234567890). Signal accepts both.
Create a Conversion Action
A conversion action tells Google Ads what type of conversion you are tracking and how to count it.
- In Google Ads, navigate to Tools & Settings > Measurement > Conversions.
- Click + New Conversion Action.
- Select Import as the conversion source.
- Choose Other data sources or CRMs > Track conversions from clicks.
- Name the conversion action (e.g. “Website Purchase”, “Lead Form Submit”).
- Set the Category (e.g. Purchase, Lead, Sign-up), Value, and Count (one per click or every).
- Click Create and Continue.
- After creation, find the Conversion Action ID — this is a numeric ID visible in the conversion action’s details page URL or under the tag setup instructions.
The Conversion Action ID is a numeric value (e.g. 987654321), not the conversion action name. You can find it in the URL when viewing the conversion action: ...&ctId=987654321.
Enable Enhanced Conversions
Enhanced Conversions allow Google to match server-uploaded conversions to signed-in Google users using hashed first-party data (email, phone, address). This significantly improves attribution when gclid is not available.
- In Google Ads, go to Tools & Settings > Measurement > Conversions.
- Click on the conversion action you created.
- Expand the Enhanced conversions section.
- Toggle Enhanced conversions on.
- Select Google Ads API as the method for sending enhanced conversion data.
- Accept the customer data terms.
- Click Save.
Enhanced Conversions are optional but strongly recommended. Without them, conversions can only be attributed via click identifiers (gclid, wbraid, gbraid). With Enhanced Conversions enabled, Signal automatically hashes and sends user data alongside the conversion for improved match rates.
Set Up OAuth2 Credentials
Google Ads API requires OAuth2 authentication. You need to create credentials in Google Cloud Console and generate a refresh token.
Create OAuth Client Credentials:
- Go to Google Cloud Console.
- Create a new project or select an existing one.
- Navigate to APIs & Services > Library.
- Search for Google Ads API and click Enable.
- Go to APIs & Services > Credentials.
- Click Create Credentials > OAuth Client ID.
- If prompted, configure the OAuth consent screen first (External or Internal depending on your organisation).
- Select Web application as the application type.
- Under Authorized redirect URIs, add:
https://developers.google.com/oauthplayground - Click Create.
- Note the Client ID and Client Secret from the confirmation dialog.
Generate a Refresh Token
The refresh token gives Signal ongoing access to your Google Ads account without requiring repeated sign-in.
- Go to the OAuth 2.0 Playground.
- Click the gear icon (Settings) in the top-right corner.
- Check Use your own OAuth credentials.
- Enter your Client ID and Client Secret from the previous step.
- Close the settings panel.
- In the left panel under Step 1, find and expand Google Ads API (or manually enter the scope:
https://www.googleapis.com/auth/adwords). - Select the
https://www.googleapis.com/auth/adwordsscope. - Click Authorize APIs.
- Sign in with a Google account that has admin access to the Google Ads account you want to track conversions for.
- Grant the requested permissions.
- Back in the Playground, under Step 2, click Exchange authorization code for tokens.
- Copy the Refresh Token from the response.
Store your OAuth credentials securely. The refresh token provides ongoing access to your Google Ads account and does not expire unless explicitly revoked. If compromised, revoke it immediately in Google Cloud Console > Credentials.
Link Manager Account (If Using MCC)
If you manage multiple Google Ads accounts through a Manager Account (MCC) and want to upload conversions to a child account:
- In your MCC dashboard, go to Tools & Settings > Setup > Linked accounts.
- Ensure the target account is linked and the link is accepted.
- Use the MCC Customer ID as the
customer_idin Signal, and the conversion action from the child account.
When using an MCC, the OAuth credentials must be for a user with access to the MCC. Signal will use the MCC customer ID to authenticate and upload conversions to the linked child account.
Configuration
| Field | Required | Type | Description |
|---|---|---|---|
customer_id | Yes | string | Your Google Ads customer ID (digits only, no dashes). Templated into the upload endpoint URL. Found top-right in the Google Ads console. |
developer_token | Yes | secret | Your Google Ads API developer token (API Center under the manager/MCC account). Sent as the developer-token request header; the API rejects all calls without it. |
conversion_action_id | Yes | string | The numeric ID of the conversion action in Google Ads (Tools > Conversions). Used to build the conversion_action resource name customers/{customer_id}/conversionActions/{conversion_action_id}. |
client_id | Yes | string | OAuth2 client ID from the Google Cloud project authorised for the Google Ads API. Sent in the refresh_token grant when minting access tokens. |
client_secret | Yes | secret | OAuth2 client secret paired with the client ID. Required by Google’s token endpoint for the refresh_token grant. |
refresh_token | Yes | secret | Long-lived OAuth2 refresh token for the authorised Google Ads user, with the https://www.googleapis.com/auth/adwords scope. Exchanged for short-lived access tokens at delivery time. |
Configure in Signal
Management UI Setup
- Go to Integrations > Add Integration > Google Ads Conversions and choose a preset.
- Enter your Customer ID (digits only, no dashes) and Conversion Action ID.
- Enter the OAuth Client ID, Client Secret and Refresh Token, and the Developer Token.
- Select consent categories (typically
advertising). - Click Save.
Management API Setup
curl -X POST http://localhost:8084/v1/admin/integration-catalog/google_ads/install \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Google Ads - Purchases",
"preset": "ecommerce",
"config": {
"customer_id": "1234567890",
"conversion_action_id": "987654321",
"developer_token": "YOUR_DEVELOPER_TOKEN",
"client_id": "123456789-abcdefg.apps.googleusercontent.com",
"client_secret": "GOCSPX-abcdefghijklmnop",
"refresh_token": "1//0abcdefghijklmnopqrstuvwxyz"
}
}'Presets
| Preset | Conversions it uploads |
|---|---|
Retail (ecommerce) | Purchases (order ID, value, currency), add-to-cart and checkout. |
B2B / SaaS (saas) | Sign-ups, leads and paid subscriptions. |
| Automotive | Finance quotes, trade-in valuations, test drives, service bookings, dealer enquiries and brochure downloads. |
| Insurance | Policy sales (purchase, policy_purchased) with the policy number as order ID and the premium as value. |
One integration uploads to one conversion action (conversion_action_id). To report leads and sales to different conversion actions, add one integration per conversion action and keep the events each should send (see Multiple conversion actions).
Identity signals
| Signal | Field | Notes |
|---|---|---|
| Google click ID | gclid | From the landing page URL. Exactly one click ID is sent per conversion: gclid, else gbraid, else wbraid. |
| App-to-web click ID | gbraid | Sent only when there is no gclid. |
| Web-to-app click ID | wbraid | Sent only when there is neither gclid nor gbraid. |
user_identifiers[].hashed_email | Normalised SHA-256 (trimmed, lower-cased). | |
| Phone | user_identifiers[].hashed_phone_number | Normalised SHA-256 of the E.164 number. |
Google accepts only email and phone as user identifiers on click conversion uploads, so names and addresses are not sent. Google only matches on these values, so they are never sent in plain text. See Personal data by integration.
API endpoint
POST https://googleads.googleapis.com/v17/customers/{{customer_id}}:uploadClickConversionsAllow egress to googleads.googleapis.com from the network your delivery workers run on.
Presets and event mapping
Every conversion carries conversion_action (customers/<customer_id>/conversionActions/<conversion_action_id>, from the integration settings), conversion_date_time (yyyy-mm-dd hh:mm:ss+00:00), one click ID, the hashed user identifiers and consent.ad_user_data. order_id is transaction_id, else the legacy order_id; on automotive leads it is the event’s own ID, because Google deduplicates conversions on it and a vehicle or dealer ID would merge different people’s test drives into one. conversion_value is value (including tax and shipping, as the event specs define it), else the legacy revenue; currency_code is upper-cased. The event names in the tables are the Google Ads conversion categories the preset is written for; they are not sent.
Retail preset (ecommerce)
Purchases (order ID, value and currency), add-to-cart and checkout conversions with enhanced conversions data.
| Signal event | Google Ads category event | Fields the preset sets |
|---|---|---|
add_to_cart | ADD_TO_CART | conversion_value, currency_code. |
begin_checkout | BEGIN_CHECKOUT | conversion_value, currency_code. |
purchase | PURCHASE | order_id, conversion_value, currency_code. Sent only when properties.currency is set. |
B2B / SaaS preset (saas)
Sign-up, lead and paid-subscription conversions with enhanced conversions.
| Signal event | Google Ads category event | Fields the preset sets |
|---|---|---|
sign_up | SIGNUP | conversion_value, currency_code. |
generate_lead | SUBMIT_LEAD_FORM | conversion_value, currency_code. |
subscription_started | SUBSCRIBE_PAID | order_id, conversion_value, currency_code. Sent only when properties.currency is set. |
Automotive preset (automotive)
Vehicle marketing conversions: finance quotes and trade-in valuations, test drives and service bookings, dealer enquiries and brochure downloads; order_id is the event’s own ID (Google’s per-conversion dedup key), never the vehicle or dealer ID.
| Signal event | Google Ads category event | Fields the preset sets |
|---|---|---|
brochure_downloaded | DOWNLOAD | order_id. |
finance_quote_requested | REQUEST_QUOTE | conversion_value, currency_code, order_id. |
trade_in_valued | REQUEST_QUOTE | conversion_value, currency_code, order_id. |
test_drive_booked | BOOK_APPOINTMENT | order_id. |
dealer_contacted | CONTACT | order_id. |
service_appointment_booked | BOOK_APPOINTMENT | order_id, conversion_value, currency_code. |
Insurance preset (insurance)
Policy sales as a Google Ads offline click conversion (Bennetts): purchase and policy_purchased with the policy number as order_id, the premium as conversion_value and the currency, sent only for a click (gclid, else gbraid, else wbraid) with email and phone as normalised SHA-256 hashes. One integration uploads to one conversion action: send quote leads through a second Google Ads integration with its own conversion action (lead_gen events).
| Signal event | Google Ads category event | Fields the preset sets |
|---|---|---|
purchase | PURCHASE | order_id, conversion_value, currency_code. Sent only when properties.currency is set. |
policy_purchased | PURCHASE | order_id, conversion_value, currency_code. Sent only when properties.currency is set. |
Example: purchase
A Retail purchase from a known visitor after a Google Ads click (the request body; developer-token and the OAuth access token travel as headers):
{
"conversions": [
{
"consent": {
"ad_user_data": "GRANTED"
},
"conversion_action": "customers/SNT-cfg-customer_id/conversionActions/SNT-cfg-conversion_action_id",
"conversion_date_time": "2026-09-23 09:30:00+00:00",
"conversion_value": 49995,
"currency_code": "GBP",
"gclid": "g-123",
"order_id": "ORD-10042",
"user_identifiers": [
{
"hashed_email": "86e0b9e56c17cc4d12387e1949b85053fbe73bc3ce5a1188713a9d300cc6133d",
"hashed_phone_number": "a8acc3a90a7b4e4dc65e93db9240ed26523050ef754d63b75b5161de76781436"
}
]
}
]
}Multiple conversion actions
Add one Google Ads integration per conversion action, each with its own conversion_action_id, and keep only the events that belong to that action in the installed blueprint (for example, one integration for purchase and one for generate_lead).
Enhanced Conversions
Enhanced conversions add the hashed email and phone to the click conversion so Google can match it to a signed-in user when the click ID alone is not enough. Call datafly.identify() when a visitor gives you their details:
datafly.identify("user-123", { email: "jane@example.com", phone: "+44 20 7946 0000" });
datafly.track("purchase", { transaction_id: "ORD-001", value: 89.99, currency: "GBP", items: [...] });Signal trims and lower-cases the email, shapes the phone to E.164 and hashes both with SHA-256 inside your deployment before the upload.
Consent Mode
Google asks for the visitor’s consent to use their data for advertising on every click conversion from the EEA and UK. Signal sends consent.ad_user_data: GRANTED when the visitor’s canonical marketing consent is granted, DENIED when it is refused. ad_personalization is not sent: Google accepts it only on customer match and store sales uploads, not on click conversions.
Testing
After configuring the integration, verify that conversions are arriving in Google Ads:
- Trigger a test conversion on your site (e.g. complete a test purchase or submit a lead form).
- In Google Ads, navigate to Tools & Settings > Measurement > Conversions.
- Click on your conversion action.
- Check the Diagnostics tab — recent uploads should appear within a few hours.
- Conversion source should display as “Uploaded (from API)” or “Website (Enhanced conversions from API)”.
- In the All conversions column, you should see the uploaded conversion count increase.
Google Ads may take up to 24 hours to process and display uploaded conversions. For real-time verification, check the Signal Management UI delivery logs to confirm that the API call returned a success response.
To validate in the Management UI:
- Go to Integrations and select your Google Ads integration.
- Check the Delivery Log for recent events.
- A
200response code withpartial_failure_erroras empty confirms successful delivery.
Troubleshooting
| Problem | Cause | Solution |
|---|---|---|
AUTHENTICATION_ERROR | Invalid or expired OAuth credentials. | Regenerate the refresh token using the OAuth Playground (Step 5 above) and update the credentials in Signal. |
AUTHORIZATION_ERROR | The authenticated user does not have access to the Google Ads account. | Ensure the Google account used in the OAuth flow has admin access to the target Google Ads account. |
CUSTOMER_NOT_FOUND | Incorrect customer ID. | Verify the customer ID matches the account in Google Ads. If using MCC, ensure the child account is linked. |
CONVERSION_ACTION_NOT_FOUND | Incorrect conversion action ID, or the action was deleted. | Check the conversion action exists in Google Ads under Tools > Conversions. Use the numeric ID, not the name. |
CLICK_NOT_FOUND | The gclid is invalid or expired (>90 days). | Ensure gclid values are captured correctly. Google Click IDs expire after 90 days. |
| Conversions not appearing | Reporting delay. | Google Ads can take up to 24 hours to display uploaded conversions. Check Signal delivery logs for API success. |
TOO_MANY_CONVERSIONS_IN_REQUEST | Batch size exceeds Google’s limit. | Signal handles batching automatically. If this persists, contact support. |
DUPLICATE_CONVERSION | Same order_id or gclid + timestamp combination sent twice. | This is expected behaviour if events are retried. Google deduplicates on order_id. |
| Enhanced conversions not matching | User identifiers not provided or incorrectly formatted. | Ensure datafly.identify() is called before the conversion event with at least an email or phone number. |
Rate Limits
| Setting | Default |
|---|---|
rate_limit_rps | 50 |
rate_limit_burst | 100 |
Google Ads API has per-account rate limits that vary by account tier. The defaults above are conservative. If you have a high-volume account with elevated API quotas, you can increase these values in the integration’s advanced settings.
Signal automatically batches conversion uploads and handles retries with exponential backoff. If rate limits are hit, events are queued and retried without data loss.