Skip to main content

Google Data Manager API

Server Source code Package

The Google Data Manager API destination package provides server-side integration for sending conversion events and audience data to Google's advertising ecosystem through a single unified API endpoint.

Where this fits

Google Data Manager API is a server destination in the walkerOS flow:

Sends conversion events server-side to Google Ads, Display & Video 360, and GA4 through a single unified API.

Installation

npm install @walkeros/server-destination-datamanager
import { startFlow } from '@walkeros/collector';
import { destinationDataManager } from '@walkeros/server-destination-datamanager';

await startFlow({
destinations: {
  datamanager: {
    code: destinationDataManager,
    config: {
      // Service account credentials
      credentials: {
        client_email: process.env.GOOGLE_CLIENT_EMAIL,
        private_key: process.env.GOOGLE_PRIVATE_KEY,
      },
      settings: {
        destinations: [
          {
            operatingAccount: {
              accountId: '123-456-7890',
              accountType: 'GOOGLE_ADS',
            },
            productDestinationId: 'AW-CONVERSION-123',
          },
        ],
      },
    },
  },
},
});

Configuration

This destination uses the standard destination config wrapper (consent, data, env, id, ...). For the shared fields see destination configuration. Package-specific fields live under config.settings and are listed below.

Settings

PropertyTypeDescriptionMore
credentialscredentialsService account credentials (client_email + private_key). Recommended for serverless environments. (deprecated: use config.credentials)
client_email*stringService account email
private_key*stringService account private key (PEM format)
keyFilenamestringPath to service account JSON file. For local development or environments with filesystem access.
scopesArray<string>OAuth scopes for Data Manager API. Defaults to datamanager scope.
destinations*Array<object>Array of destination accounts and conversion actions/user lists (max 10)
eventSourceEvent source for all events. Defaults to WEB. Values: WEB, APP, IN_STORE, PHONE, OTHER
batchSizeintegerMaximum number of events to batch before sending (max 2000, like 100)
batchIntervalintegerTime in milliseconds to wait before auto-flushing batch
validateOnlybooleanIf true, validate request without ingestion (testing mode)
urlstringOverride API endpoint for testing
consentconsentRequest-level consent for all events
adUserDataConsent for data collection and use
adPersonalizationConsent for ad personalization
testEventCodestringTest event code for debugging
logLevelLog level for debugging (debug shows all API calls)
userDataRecord<string, any>Guided helper: User data mapping for all events
userIdanyGuided helper: First-party user ID for all events
clientIdanyGuided helper: GA4 client ID for all events
appInstanceIdanyGuided helper: GA4 app instance ID (Firebase) for all events
sessionAttributesanyGuided helper: Privacy-safe attribution for all events
consentAdUserDatastring | booleanConsent mapping: Field name from event.consent (like 'marketing') or static boolean value
consentAdPersonalizationstring | booleanConsent mapping: Field name from event.consent (like 'targeting') or static boolean value
* Required fields

Mapping

This package does not define custom rule-level settings. For the standard rule fields (consent, condition, data, batch, name, policy) see mapping.

Examples

Page view

A page view is sent to Data Manager as a page_view event tied to the walker user id.

Event
{
  "name": "page view",
  "data": {
    "title": "Pricing",
    "url": "https://example.com/pricing"
  },
  "context": {
    "dev": [
      "test",
      1
    ]
  },
  "globals": {
    "pagegroup": "docs"
  },
  "custom": {
    "completely": "random"
  },
  "user": {
    "id": "visitor-55"
  },
  "nested": [
    {
      "entity": "child",
      "data": {
        "is": "subordinated"
      }
    }
  ],
  "consent": {
    "functional": true
  },
  "id": "ev-1700000902000",
  "trigger": "load",
  "entity": "page",
  "action": "view",
  "timestamp": 1700000902000,
  "timing": 3.14,
  "source": {
    "count": 1,
    "trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
    "type": "express",
    "platform": "server"
  }
}
Mapping
{
  "name": "page_view",
  "data": {
    "map": {
      "transactionId": "id",
      "eventName": {
        "value": "page_view"
      },
      "userId": "user.id"
    }
  }
}
Out
fetch("https://datamanager.googleapis.com/v1/events:ingest", {
  "method": "POST",
  "headers": {
    "Authorization": "Bearer ya29.c.test_token",
    "Content-Type": "application/json"
  },
  "body": "{\"events\":[{\"eventTimestamp\":\"2023-11-14T22:28:22.000Z\",\"transactionId\":\"ev-1700000902000\",\"userId\":\"visitor-55\",\"eventName\":\"page_view\",\"eventSource\":\"WEB\"}],\"destinations\":[{\"operatingAccount\":{\"accountId\":\"123-456-7890\",\"accountType\":\"GOOGLE_ADS\"},\"productDestinationId\":\"AW-CONVERSION-123\"}],\"encoding\":\"HEX\"}"
})

Lead

A demo request form submission is sent to Data Manager as a generate_lead conversion with a hashed email.

Event
{
  "name": "form submit",
  "data": {
    "type": "demo-request"
  },
  "context": {
    "dev": [
      "test",
      1
    ]
  },
  "globals": {
    "lang": "elb"
  },
  "custom": {
    "completely": "random"
  },
  "user": {
    "email": "prospect@example.com"
  },
  "nested": [
    {
      "entity": "child",
      "data": {
        "is": "subordinated"
      }
    }
  ],
  "consent": {
    "functional": true
  },
  "id": "ev-1700000901000",
  "trigger": "test",
  "entity": "form",
  "action": "submit",
  "timestamp": 1700000901000,
  "timing": 3.14,
  "source": {
    "count": 1,
    "trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
    "type": "express",
    "platform": "server"
  }
}
Mapping
{
  "name": "generate_lead",
  "data": {
    "map": {
      "transactionId": "id",
      "eventName": {
        "value": "generate_lead"
      },
      "conversionValue": {
        "value": 10
      },
      "currency": {
        "value": "USD"
      },
      "email": "user.email"
    }
  }
}
Out
fetch("https://datamanager.googleapis.com/v1/events:ingest", {
  "method": "POST",
  "headers": {
    "Authorization": "Bearer ya29.c.test_token",
    "Content-Type": "application/json"
  },
  "body": "{\"events\":[{\"eventTimestamp\":\"2023-11-14T22:28:21.000Z\",\"transactionId\":\"ev-1700000901000\",\"userData\":{\"userIdentifiers\":[{\"emailAddress\":\"395ec5f334be0ab5b28568a1e7f6ed5ea80e443fb1ce3d803340586a3df46642\"}]},\"conversionValue\":10,\"currency\":\"USD\",\"eventName\":\"generate_lead\",\"eventSource\":\"WEB\"}],\"destinations\":[{\"operatingAccount\":{\"accountId\":\"123-456-7890\",\"accountType\":\"GOOGLE_ADS\"},\"productDestinationId\":\"AW-CONVERSION-123\"}],\"encoding\":\"HEX\"}"
})

Purchase

A completed order is posted to Google Data Manager as a purchase conversion with hashed user identifiers.

Event
{
  "name": "order complete",
  "data": {
    "id": "ORD-600",
    "total": 149.99,
    "currency": "EUR"
  },
  "context": {
    "shopping": [
      "complete",
      0
    ]
  },
  "globals": {
    "pagegroup": "shop"
  },
  "custom": {
    "completely": "random"
  },
  "user": {
    "id": "user-abc",
    "email": "buyer@example.com"
  },
  "nested": [
    {
      "entity": "product",
      "data": {
        "id": "ers",
        "name": "Everyday Ruck Snack",
        "color": "black",
        "size": "l",
        "price": 420
      },
      "context": {
        "shopping": [
          "complete",
          0
        ]
      },
      "nested": []
    },
    {
      "entity": "product",
      "data": {
        "id": "cc",
        "name": "Cool Cap",
        "size": "one size",
        "price": 42
      },
      "context": {
        "shopping": [
          "complete",
          0
        ]
      },
      "nested": []
    },
    {
      "entity": "gift",
      "data": {
        "name": "Surprise"
      },
      "context": {
        "shopping": [
          "complete",
          0
        ]
      },
      "nested": []
    }
  ],
  "consent": {
    "functional": true
  },
  "id": "ev-1700000900000",
  "trigger": "load",
  "entity": "order",
  "action": "complete",
  "timestamp": 1700000900000,
  "timing": 3.14,
  "source": {
    "count": 1,
    "trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
    "type": "express",
    "platform": "server"
  }
}
Mapping
{
  "name": "purchase",
  "data": {
    "map": {
      "transactionId": "data.id",
      "conversionValue": "data.total",
      "currency": {
        "key": "data.currency",
        "value": "USD"
      },
      "eventName": {
        "value": "purchase"
      },
      "userId": "user.id",
      "email": "user.email"
    }
  }
}
Out
fetch("https://datamanager.googleapis.com/v1/events:ingest", {
  "method": "POST",
  "headers": {
    "Authorization": "Bearer ya29.c.test_token",
    "Content-Type": "application/json"
  },
  "body": "{\"events\":[{\"eventTimestamp\":\"2023-11-14T22:28:20.000Z\",\"transactionId\":\"ORD-600\",\"userId\":\"user-abc\",\"userData\":{\"userIdentifiers\":[{\"emailAddress\":\"6a6c26195c3682faa816966af789717c3bfa834eee6c599d667d2b3429c27cfd\"}]},\"conversionValue\":149.99,\"currency\":\"EUR\",\"eventName\":\"purchase\",\"eventSource\":\"WEB\"}],\"destinations\":[{\"operatingAccount\":{\"accountId\":\"123-456-7890\",\"accountType\":\"GOOGLE_ADS\"},\"productDestinationId\":\"AW-CONVERSION-123\"}],\"encoding\":\"HEX\"}"
})

Guided mapping helpers

Define common fields once in Settings instead of repeating them in every event mapping:

await startFlow({
destinations: {
  datamanager: {
    code: destinationDataManager,
    config: {
      credentials: { /* ... */ },
      settings: {
        destinations: [...],

        // Guided helpers (apply to all events)
        userData: {
          email: 'user.mail',
          phone: 'data.phone',
        },
        userId: 'user.id',
        clientId: 'user.device',                  // GA4 web stream
        appInstanceId: 'user.appInstanceId',      // GA4 app stream (Firebase)
        sessionAttributes: 'context.sessionAttributes',

        // Consent mapping
        consentAdUserData: 'marketing',
        consentAdPersonalization: 'personalization',
      },
    },
  },
},
});

Consent mapping: Map your consent field names (string) or use static values (boolean). Event mappings always override Settings helpers.

Store sales (IN_STORE)

For physical store conversions, set eventSource: 'IN_STORE' on the destination and map a storeId in the event mapping. The destination wraps it into the API's required eventLocation.storeId shape.

mapping: {
order: {
  complete: {
    name: 'purchase',
    data: {
      map: {
        transactionId: 'data.id',
        conversionValue: 'data.total',
        currency: 'data.currency',
        storeId: 'data.storeId',
        email: 'user.email', // hashed PII still applies
      },
    },
  },
},
}

Hash encoding

Every request is sent with encoding: 'HEX'. Google requires the field whenever the payload contains hashed userData. The value is pinned because all hashing in this destination produces lowercase hex SHA-256 digests; exposing encoding as a setting would risk silently mismatched identifiers.

💡 Need implementation support?
elbwalker offers hands-on support: setup review, measurement planning, destination mapping, and live troubleshooting. Book a 2-hour session (€399)