Google Data Manager
Send offline conversions to a Campaign Manager 360 (CM360) Floodlight activity, or to a Google Ads conversion action, through Google’s Data Manager API.
Use this integration rather than CM360 Floodlights for new work. Google advises against building new offline conversion workflows on the Campaign Manager 360 API and recommends the Data Manager API instead. Do not send the same Floodlight activity through both integrations: the two APIs deduplicate differently, so a conversion sent through both is counted twice.
This integration is in beta (version 0.1.0).
Why route conversions through Datafly Signal
- Conversions survive ad blockers and tracking prevention. Signal sends the conversion from your own infrastructure, so the conversions CM360 and Google Ads optimise against are not filtered by the browser.
- Email and phone number are hashed before leaving your infrastructure. Signal normalises (trim and lowercase; phone numbers to E.164) and applies SHA-256 inside your own deployment, and sends hex-encoded digests.
- Click identifiers are captured and replayed. Signal stores
gclid,dclid,gbraidandwbraidfirst-party at collection time and attaches them to the conversion. - Delivery is batched, rate-limited and retried. Up to 2,000 conversions per request, within Google’s per-project request limit, with retries on transient failures.
- One consent decision governs every destination. Each conversion carries the visitor’s ad user data and ad personalisation consent.
How it works
Each conversion is sent to events:ingest:
POST https://datamanager.googleapis.com/v1/events:ingestAllow egress to datamanager.googleapis.com and oauth2.googleapis.com from the network your delivery workers run on.
One integration sends to one destination: one Floodlight activity, or one Google Ads conversion action. To count a second conversion type (for example leads as well as purchases), install the integration a second time with that activity’s ID and enable only its events.
Google processes requests fast-fail: if any conversion in a request has an error, the whole request is rejected. Signal isolates the conversion Google rejected and sends the rest again, and only the rejected conversion goes to the dead-letter queue.
Prerequisites
Enable the Data Manager API
In the Google Cloud console, choose (or create) a project and enable the Data Manager API.
Create a service account and key
Under IAM & Admin > Service Accounts, create a service account (for example datafly-signal-dm), grant it the Service Usage Consumer role (roles/serviceusage.serviceUsageConsumer) on the project, and create a JSON key. Signal requests the https://www.googleapis.com/auth/datamanager scope with this key.
Store the key securely and never commit it to source control. An existing service account, such as the one used by CM360 Floodlights, can be reused: Signal requests the Data Manager scope for each token. Its project must have the Data Manager API enabled.
Give the service account access to the destination
- Campaign Manager 360: in CM360, go to Admin > User profiles and add the service account email as a user profile with access to the advertiser, using a user role that includes the Insert offline conversions permission.
- Google Ads: add the service account email as a user on the Google Ads account that owns the conversion action, or on its manager account.
Collect the IDs
- CM360: the Floodlight configuration ID (the advertiser ID, or the parent advertiser’s ID if the advertiser uses a parent’s Floodlight configuration) and the Floodlight activity ID (Floodlight > Activities, the number next to the activity name).
- Google Ads: the customer ID of the account that owns the conversion action, and the conversion action ID (Goals > Conversions > the action > Details, “Conversion type ID”). The conversion action’s source must be Website (Import from clicks). If the service account’s access is through a manager account, also note the manager account’s customer ID.
Configuration
| Field | Type | Required | Description |
|---|---|---|---|
account_type | select | Yes | FLOODLIGHT_CONFIG (Campaign Manager 360 Floodlight, the default) or GOOGLE_ADS. |
operating_account_id | string | Yes | Floodlight configuration ID, or Google Ads customer ID. Dashes are removed automatically. |
product_destination_id | string | Yes | Floodlight activity ID, or Google Ads conversion action ID. |
login_account_id | string | No | Google Ads manager account ID, only when access is through a manager account. Leave blank for CM360: Google rejects a Floodlight request whose login account differs from the operating account. |
service_account_json | secret | Yes | The service account JSON key. |
validate_only | boolean | No | When on, Google validates each request and returns any errors but records nothing. Use it for the first test. |
Configure in Signal
- Navigate to Integrations in the sidebar and open the Integration Library tab.
- Find Google Data Manager under Advertising.
- Choose a preset, enter the fields above, and turn Validate only on for the first test.
- Click Install Integration, then add it to a pipeline.
Presets
| Preset | Enabled events | Notes |
|---|---|---|
| Default | purchase | generate_lead is included but disabled: enable it in a second integration pointed at a lead activity. |
| Retail | purchase | Adds cart items (merchantProductId from item_id, quantity, unitPrice from price). Floodlight expects Merchant Center product IDs; Google reports other IDs as warnings, not errors. |
| Insurance | purchase, policy_purchased | The policy number is the transaction ID, so a site that fires both counts once. quote_requested, quote_completed, generate_lead and lead_submitted are included but disabled, one integration per funnel stage. |
What Signal sends
| Data Manager field | Signal source |
|---|---|
destinations[0] | The integration’s configuration (operating account, login account, product destination ID). |
encoding | Always HEX. |
validateOnly | The Validate only setting. |
events[].transactionId | Purchase events: properties.transaction_id, else properties.order_id. Quote events: properties.quote_id, else properties.transaction_id. Lead events: properties.transaction_id, else properties.lead_id. In every case the Signal message ID is the last resort, so a replayed event deduplicates. Google deduplicates on activity + transaction ID, so every distinct conversion needs a distinct value. |
events[].eventTimestamp | The event timestamp, RFC 3339 (for example 2026-09-22T14:05:06Z). |
events[].conversionValue, currency | properties.value (else properties.revenue) as a number; properties.currency upper-cased. |
events[].eventSource | WEB. |
events[].adIdentifiers | gclid, dclid, gbraid, wbraid captured by Signal. |
events[].userData.userIdentifiers | emailAddress and phoneNumber: normalised, SHA-256, hex. An unusable phone number is left out rather than sent. |
events[].consent | adUserData and adPersonalization: CONSENT_GRANTED when the visitor granted Signal’s marketing consent category, otherwise CONSENT_DENIED. |
Floodlight matches a conversion only if it carries a click or device identifier (gclid or dclid from Signal); hashed email and phone are extra match signals. Google Ads also accepts hashed email or phone alone.
Example request
{
"destinations": [{
"operatingAccount": {"accountId": "98765432", "accountType": "FLOODLIGHT_CONFIG"},
"loginAccount": {"accountId": "98765432", "accountType": "FLOODLIGHT_CONFIG"},
"productDestinationId": "12345678"
}],
"encoding": "HEX",
"validateOnly": true,
"events": [{
"transactionId": "POL-55012",
"eventTimestamp": "2026-09-22T14:05:06Z",
"conversionValue": 480.5,
"currency": "GBP",
"eventSource": "WEB",
"adIdentifiers": {"gclid": "Cj0KCQjw..."},
"userData": {"userIdentifiers": [
{"emailAddress": "<sha256 hex>"},
{"phoneNumber": "<sha256 hex>"}
]},
"consent": {"adUserData": "CONSENT_GRANTED", "adPersonalization": "CONSENT_GRANTED"}
}]
}Testing
Validate first
With Validate only on, send a test conversion. Google checks the request and returns errors without recording anything. Requests sent in this mode have no diagnostics.
Check the request in Signal
With debug on for the pipeline, the Event Debugger shows the request Signal sent and Google’s response, including its requestId. A rejected request’s error, with the request ID and Google’s field violations, is on the delivery failure and its dead-letter record.
Send one real conversion
Turn Validate only off and send one conversion with a known gclid or dclid, ideally to a dedicated test Floodlight activity. In CM360, check the activity’s reporting (offline conversions can take several hours to appear) or Google’s diagnostics for the request ID.
Troubleshooting
| Symptom | Cause | Resolution |
|---|---|---|
403 PERMISSION_DENIED | The service account has no access to the destination, or the Data Manager API is not enabled | Enable the API in the project; add the service account in CM360 (user profile with Insert offline conversions) or Google Ads. |
400 OPERATING_ACCOUNT_LOGIN_ACCOUNT_MISMATCH | A login account was set for a Floodlight destination | Leave the manager account field blank for CM360. |
400 INVALID_ARGUMENT | A field Google rejects; the whole request fails | Read the field violation in the delivery error and fix the source data or configuration. |
| Duplicate conversions | The same conversion sent through both this integration and CM360 Floodlights, or no stable transaction ID | Send each Floodlight activity through one integration only; send transaction_id on conversions. |
429 RESOURCE_EXHAUSTED | Over Google’s request limit for the project (300 per minute, 100,000 per day) | Signal retries; if persistent, use a separate project per high-volume integration. |
Google’s references: Floodlight offline conversions, upgrading from the CM360 API, events:ingest and limits.