@walkeros/server-destination-datamanager 0.0.0-next-20251219153324
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +820 -0
- package/dist/dev.d.mts +233 -0
- package/dist/dev.d.ts +233 -0
- package/dist/dev.js +1 -0
- package/dist/dev.js.map +1 -0
- package/dist/dev.mjs +1 -0
- package/dist/dev.mjs.map +1 -0
- package/dist/examples/index.d.mts +218 -0
- package/dist/examples/index.d.ts +218 -0
- package/dist/examples/index.js +320 -0
- package/dist/examples/index.mjs +297 -0
- package/dist/index.d.mts +388 -0
- package/dist/index.d.ts +388 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1 -0
- package/dist/index.mjs.map +1 -0
- package/dist/schemas.d.mts +6 -0
- package/dist/schemas.d.ts +6 -0
- package/dist/schemas.js +1 -0
- package/dist/schemas.js.map +1 -0
- package/dist/schemas.mjs +1 -0
- package/dist/schemas.mjs.map +1 -0
- package/package.json +66 -0
package/README.md
ADDED
|
@@ -0,0 +1,820 @@
|
|
|
1
|
+
# @walkeros/server-destination-datamanager
|
|
2
|
+
|
|
3
|
+
Google Data Manager server destination for walkerOS - send conversion events and
|
|
4
|
+
audience data to Google Ads, Display & Video 360, and Google Analytics 4 through
|
|
5
|
+
a single unified API.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Multi-platform reach**: Single API call sends data to Google Ads, DV360, and
|
|
10
|
+
GA4
|
|
11
|
+
- **Privacy-first**: SHA-256 hashing of PII with Gmail-specific email
|
|
12
|
+
normalization
|
|
13
|
+
- **DMA compliance**: Built-in consent management for EEA/UK/Switzerland
|
|
14
|
+
- **Explicit mapping**: Transform walkerOS events to Data Manager format using
|
|
15
|
+
declarative mapping rules
|
|
16
|
+
- **Type-safe**: Full TypeScript support with comprehensive type definitions
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install @walkeros/server-destination-datamanager
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
### Minimal Configuration (AWS Lambda / Serverless)
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { destinationDataManager } from '@walkeros/server-destination-datamanager';
|
|
30
|
+
import { startFlow } from '@walkeros/collector';
|
|
31
|
+
|
|
32
|
+
const { collector, elb } = await startFlow({
|
|
33
|
+
destinations: {
|
|
34
|
+
datamanager: {
|
|
35
|
+
...destinationDataManager,
|
|
36
|
+
config: {
|
|
37
|
+
settings: {
|
|
38
|
+
// Service account credentials from environment variables
|
|
39
|
+
credentials: {
|
|
40
|
+
client_email: process.env.GOOGLE_CLIENT_EMAIL!,
|
|
41
|
+
private_key: process.env.GOOGLE_PRIVATE_KEY!.replace(/\\n/g, '\n'),
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
// Destination accounts
|
|
45
|
+
destinations: [
|
|
46
|
+
{
|
|
47
|
+
operatingAccount: {
|
|
48
|
+
accountId: '123-456-7890',
|
|
49
|
+
accountType: 'GOOGLE_ADS',
|
|
50
|
+
},
|
|
51
|
+
productDestinationId: 'AW-CONVERSION-123',
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Track a conversion
|
|
61
|
+
await elb('order complete', {
|
|
62
|
+
id: 'ORDER-123',
|
|
63
|
+
total: 99.99,
|
|
64
|
+
currency: 'USD',
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### GCP Cloud Functions (Application Default Credentials)
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import { destinationDataManager } from '@walkeros/server-destination-datamanager';
|
|
72
|
+
import { startFlow } from '@walkeros/collector';
|
|
73
|
+
|
|
74
|
+
// No auth config needed - ADC works automatically on GCP!
|
|
75
|
+
const { collector, elb } = await startFlow({
|
|
76
|
+
destinations: {
|
|
77
|
+
datamanager: {
|
|
78
|
+
...destinationDataManager,
|
|
79
|
+
config: {
|
|
80
|
+
settings: {
|
|
81
|
+
destinations: [
|
|
82
|
+
{
|
|
83
|
+
operatingAccount: {
|
|
84
|
+
accountId: '123-456-7890',
|
|
85
|
+
accountType: 'GOOGLE_ADS',
|
|
86
|
+
},
|
|
87
|
+
productDestinationId: 'AW-CONVERSION-123',
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Local Development
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
import { destinationDataManager } from '@walkeros/server-destination-datamanager';
|
|
101
|
+
|
|
102
|
+
const config = {
|
|
103
|
+
...destinationDataManager,
|
|
104
|
+
config: {
|
|
105
|
+
settings: {
|
|
106
|
+
// Service account JSON file
|
|
107
|
+
keyFilename: './service-account.json',
|
|
108
|
+
|
|
109
|
+
// Multiple destinations (max 10)
|
|
110
|
+
destinations: [
|
|
111
|
+
{
|
|
112
|
+
operatingAccount: {
|
|
113
|
+
accountId: '123-456-7890',
|
|
114
|
+
accountType: 'GOOGLE_ADS',
|
|
115
|
+
},
|
|
116
|
+
productDestinationId: 'AW-CONVERSION-123',
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
operatingAccount: {
|
|
120
|
+
accountId: '987654321',
|
|
121
|
+
accountType: 'GOOGLE_ANALYTICS_PROPERTY',
|
|
122
|
+
},
|
|
123
|
+
productDestinationId: 'G-XXXXXXXXXX',
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
|
|
127
|
+
// Optional settings
|
|
128
|
+
eventSource: 'WEB', // Default event source
|
|
129
|
+
batchSize: 100, // Events per batch (max 2000)
|
|
130
|
+
batchInterval: 5000, // Batch flush interval in ms
|
|
131
|
+
validateOnly: false, // Test mode (validate without ingestion)
|
|
132
|
+
testEventCode: 'TEST12345', // For debugging
|
|
133
|
+
|
|
134
|
+
// Request-level consent
|
|
135
|
+
consent: {
|
|
136
|
+
adUserData: 'CONSENT_GRANTED',
|
|
137
|
+
adPersonalization: 'CONSENT_GRANTED',
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
// Guided helpers (apply to all events)
|
|
141
|
+
userData: {
|
|
142
|
+
email: 'user.id',
|
|
143
|
+
phone: 'data.phone',
|
|
144
|
+
},
|
|
145
|
+
userId: 'user.id',
|
|
146
|
+
clientId: 'user.device',
|
|
147
|
+
sessionAttributes: 'context.sessionAttributes',
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
// Event mapping
|
|
151
|
+
mapping: {
|
|
152
|
+
order: {
|
|
153
|
+
complete: {
|
|
154
|
+
name: 'purchase',
|
|
155
|
+
data: {
|
|
156
|
+
map: {
|
|
157
|
+
transactionId: 'data.id',
|
|
158
|
+
conversionValue: 'data.total',
|
|
159
|
+
currency: { key: 'data.currency', value: 'USD' },
|
|
160
|
+
eventName: { value: 'purchase' }, // For GA4
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Authentication
|
|
171
|
+
|
|
172
|
+
The Data Manager destination uses Google Cloud service accounts with automatic
|
|
173
|
+
token refresh. The library handles token management automatically - you just
|
|
174
|
+
provide credentials and tokens are cached and refreshed as needed.
|
|
175
|
+
|
|
176
|
+
### Authentication Methods
|
|
177
|
+
|
|
178
|
+
The destination supports three authentication methods (in priority order):
|
|
179
|
+
|
|
180
|
+
1. **Inline Credentials** - Best for serverless (AWS Lambda, Docker, K8s)
|
|
181
|
+
2. **Service Account File** - Best for local development
|
|
182
|
+
3. **Application Default Credentials (ADC)** - Automatic on GCP
|
|
183
|
+
|
|
184
|
+
### 1. Inline Credentials (Recommended for Serverless)
|
|
185
|
+
|
|
186
|
+
Best for AWS Lambda, Docker, Kubernetes, and other containerized environments
|
|
187
|
+
where you manage secrets via environment variables.
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
import { destinationDataManager } from '@walkeros/server-destination-datamanager';
|
|
191
|
+
|
|
192
|
+
const config = {
|
|
193
|
+
...destinationDataManager,
|
|
194
|
+
config: {
|
|
195
|
+
settings: {
|
|
196
|
+
credentials: {
|
|
197
|
+
client_email: process.env.GOOGLE_CLIENT_EMAIL!,
|
|
198
|
+
private_key: process.env.GOOGLE_PRIVATE_KEY!.replace(/\\n/g, '\n'),
|
|
199
|
+
},
|
|
200
|
+
destinations: [
|
|
201
|
+
/* ... */
|
|
202
|
+
],
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
**Environment Variables:**
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
GOOGLE_CLIENT_EMAIL=service-account@project.iam.gserviceaccount.com
|
|
212
|
+
GOOGLE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIIE...\n-----END PRIVATE KEY-----\n"
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**Note:** Use `.replace(/\\n/g, '\n')` to convert escaped newlines from
|
|
216
|
+
environment variables.
|
|
217
|
+
|
|
218
|
+
### 2. Service Account File (Local Development)
|
|
219
|
+
|
|
220
|
+
Best for local development with filesystem access.
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
const config = {
|
|
224
|
+
...destinationDataManager,
|
|
225
|
+
config: {
|
|
226
|
+
settings: {
|
|
227
|
+
keyFilename: './service-account.json',
|
|
228
|
+
destinations: [
|
|
229
|
+
/* ... */
|
|
230
|
+
],
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### 3. Application Default Credentials (GCP)
|
|
237
|
+
|
|
238
|
+
Automatic on Google Cloud Platform - no configuration needed!
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
const config = {
|
|
242
|
+
...destinationDataManager,
|
|
243
|
+
config: {
|
|
244
|
+
settings: {
|
|
245
|
+
// No auth config needed - ADC used automatically!
|
|
246
|
+
destinations: [
|
|
247
|
+
/* ... */
|
|
248
|
+
],
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**Works automatically on:**
|
|
255
|
+
|
|
256
|
+
- Google Cloud Functions
|
|
257
|
+
- Google Cloud Run
|
|
258
|
+
- Google Compute Engine
|
|
259
|
+
- Google Kubernetes Engine
|
|
260
|
+
- Any GCP environment with default service account
|
|
261
|
+
|
|
262
|
+
**Also works with:**
|
|
263
|
+
|
|
264
|
+
- `GOOGLE_APPLICATION_CREDENTIALS` environment variable
|
|
265
|
+
- gcloud CLI: `gcloud auth application-default login`
|
|
266
|
+
|
|
267
|
+
### Creating a Service Account
|
|
268
|
+
|
|
269
|
+
1. Go to [Google Cloud Console](https://console.cloud.google.com/)
|
|
270
|
+
2. Navigate to **IAM & Admin > Service Accounts**
|
|
271
|
+
3. Click **Create Service Account**
|
|
272
|
+
4. Name: `walkeros-datamanager` (or your choice)
|
|
273
|
+
5. Grant role: **Data Manager Admin** or custom role with datamanager
|
|
274
|
+
permissions
|
|
275
|
+
6. Click **Keys > Add Key > Create New Key**
|
|
276
|
+
7. Select **JSON** format and download
|
|
277
|
+
|
|
278
|
+
**For inline credentials:**
|
|
279
|
+
|
|
280
|
+
- Extract `client_email` and `private_key` from the JSON
|
|
281
|
+
- Store in environment variables or secrets manager
|
|
282
|
+
|
|
283
|
+
**For keyFilename:**
|
|
284
|
+
|
|
285
|
+
- Set `keyFilename: '/path/to/downloaded-key.json'`
|
|
286
|
+
|
|
287
|
+
**For ADC:**
|
|
288
|
+
|
|
289
|
+
- Set `GOOGLE_APPLICATION_CREDENTIALS=/path/to/downloaded-key.json`
|
|
290
|
+
- Or deploy to GCP where ADC works automatically
|
|
291
|
+
|
|
292
|
+
### Required OAuth Scope
|
|
293
|
+
|
|
294
|
+
```
|
|
295
|
+
https://www.googleapis.com/auth/datamanager
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
This scope is automatically applied - no configuration needed.
|
|
299
|
+
|
|
300
|
+
### Token Management
|
|
301
|
+
|
|
302
|
+
- **Lifetime:** 1 hour (3600 seconds)
|
|
303
|
+
- **Refresh:** Automatic - library handles expiration
|
|
304
|
+
- **Caching:** Tokens are cached and reused until expiration
|
|
305
|
+
- **Performance:** ~10-50ms overhead per request for token validation
|
|
306
|
+
|
|
307
|
+
## Guided Mapping Helpers
|
|
308
|
+
|
|
309
|
+
Define common fields once in Settings instead of repeating them in every event
|
|
310
|
+
mapping:
|
|
311
|
+
|
|
312
|
+
```typescript
|
|
313
|
+
{
|
|
314
|
+
settings: {
|
|
315
|
+
credentials: { /* ... */ },
|
|
316
|
+
destinations: [...],
|
|
317
|
+
|
|
318
|
+
// Guided helpers (apply to all events)
|
|
319
|
+
userData: {
|
|
320
|
+
email: 'user.id',
|
|
321
|
+
phone: 'data.phone',
|
|
322
|
+
firstName: 'data.firstName',
|
|
323
|
+
lastName: 'data.lastName',
|
|
324
|
+
},
|
|
325
|
+
userId: 'user.id',
|
|
326
|
+
clientId: 'user.device',
|
|
327
|
+
sessionAttributes: 'context.sessionAttributes',
|
|
328
|
+
|
|
329
|
+
// Consent mapping (string = field name, boolean = static value)
|
|
330
|
+
consentAdUserData: 'marketing', // Read event.consent.marketing
|
|
331
|
+
consentAdPersonalization: 'personalization', // Read event.consent.personalization
|
|
332
|
+
// OR use static values:
|
|
333
|
+
// consentAdUserData: true, // Always CONSENT_GRANTED
|
|
334
|
+
},
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
**Precedence**: Settings helpers < config.data < event mapping
|
|
339
|
+
|
|
340
|
+
Event mappings always override Settings:
|
|
341
|
+
|
|
342
|
+
```typescript
|
|
343
|
+
{
|
|
344
|
+
settings: {
|
|
345
|
+
userId: 'user.id', // Default for all events
|
|
346
|
+
},
|
|
347
|
+
mapping: {
|
|
348
|
+
order: {
|
|
349
|
+
complete: {
|
|
350
|
+
data: {
|
|
351
|
+
map: {
|
|
352
|
+
userId: 'data.customerId', // Override for this event
|
|
353
|
+
},
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
},
|
|
357
|
+
},
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## Data Mapping
|
|
362
|
+
|
|
363
|
+
### Event Data Mapping
|
|
364
|
+
|
|
365
|
+
All event data must be explicitly mapped. The destination does not auto-extract
|
|
366
|
+
fields from events.
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
{
|
|
370
|
+
mapping: {
|
|
371
|
+
order: {
|
|
372
|
+
complete: {
|
|
373
|
+
name: 'purchase',
|
|
374
|
+
data: {
|
|
375
|
+
map: {
|
|
376
|
+
// Transaction data
|
|
377
|
+
transactionId: 'data.id',
|
|
378
|
+
conversionValue: 'data.total',
|
|
379
|
+
currency: { key: 'data.currency', value: 'USD' },
|
|
380
|
+
eventName: { value: 'purchase' },
|
|
381
|
+
|
|
382
|
+
// User identification
|
|
383
|
+
userId: 'user.id',
|
|
384
|
+
email: 'user.id', // Will be SHA-256 hashed
|
|
385
|
+
phone: 'data.phone', // Will be SHA-256 hashed
|
|
386
|
+
|
|
387
|
+
// Attribution identifiers
|
|
388
|
+
gclid: 'context.gclid',
|
|
389
|
+
gbraid: 'context.gbraid',
|
|
390
|
+
},
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
},
|
|
394
|
+
},
|
|
395
|
+
}
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### Attribution Identifiers
|
|
399
|
+
|
|
400
|
+
Attribution identifiers must be explicitly mapped:
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
{
|
|
404
|
+
mapping: {
|
|
405
|
+
order: {
|
|
406
|
+
complete: {
|
|
407
|
+
data: {
|
|
408
|
+
map: {
|
|
409
|
+
gclid: 'context.gclid', // From URL: ?gclid=TeSter
|
|
410
|
+
gbraid: 'context.gbraid', // iOS attribution
|
|
411
|
+
wbraid: 'context.wbraid', // Web-to-app
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
},
|
|
415
|
+
},
|
|
416
|
+
},
|
|
417
|
+
}
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### Consent Mapping
|
|
421
|
+
|
|
422
|
+
Map your consent field names to Data Manager's required fields:
|
|
423
|
+
|
|
424
|
+
```typescript
|
|
425
|
+
{
|
|
426
|
+
settings: {
|
|
427
|
+
// Map from your consent field names
|
|
428
|
+
consentAdUserData: 'marketing', // Read event.consent.marketing
|
|
429
|
+
consentAdPersonalization: 'personalization', // Read event.consent.personalization
|
|
430
|
+
},
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Your event with standard consent field names
|
|
434
|
+
await elb('order complete', { total: 99.99 }, {
|
|
435
|
+
consent: {
|
|
436
|
+
marketing: true,
|
|
437
|
+
personalization: false,
|
|
438
|
+
},
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
// Becomes Data Manager format
|
|
442
|
+
{
|
|
443
|
+
consent: {
|
|
444
|
+
adUserData: 'CONSENT_GRANTED',
|
|
445
|
+
adPersonalization: 'CONSENT_DENIED'
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
**Static values** for always-on consent:
|
|
451
|
+
|
|
452
|
+
```typescript
|
|
453
|
+
{
|
|
454
|
+
settings: {
|
|
455
|
+
consentAdUserData: true, // Always CONSENT_GRANTED
|
|
456
|
+
consentAdPersonalization: false, // Always CONSENT_DENIED
|
|
457
|
+
},
|
|
458
|
+
}
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
**Fallback**: Without consent mapping, uses `event.consent.marketing` →
|
|
462
|
+
`adUserData` and `event.consent.personalization` → `adPersonalization`.
|
|
463
|
+
|
|
464
|
+
## Event Mapping Examples
|
|
465
|
+
|
|
466
|
+
### E-commerce Purchase
|
|
467
|
+
|
|
468
|
+
```typescript
|
|
469
|
+
{
|
|
470
|
+
mapping: {
|
|
471
|
+
order: {
|
|
472
|
+
complete: {
|
|
473
|
+
name: 'purchase',
|
|
474
|
+
data: {
|
|
475
|
+
map: {
|
|
476
|
+
transactionId: 'data.id',
|
|
477
|
+
conversionValue: 'data.total',
|
|
478
|
+
currency: { key: 'data.currency', value: 'USD' },
|
|
479
|
+
eventName: { value: 'purchase' },
|
|
480
|
+
|
|
481
|
+
// Map nested products to cart items
|
|
482
|
+
cartData: {
|
|
483
|
+
map: {
|
|
484
|
+
items: {
|
|
485
|
+
loop: [
|
|
486
|
+
'nested',
|
|
487
|
+
{
|
|
488
|
+
condition: (entity) => entity.entity === 'product',
|
|
489
|
+
map: {
|
|
490
|
+
merchantProductId: 'data.id',
|
|
491
|
+
price: 'data.price',
|
|
492
|
+
quantity: { key: 'data.quantity', value: 1 },
|
|
493
|
+
},
|
|
494
|
+
},
|
|
495
|
+
],
|
|
496
|
+
},
|
|
497
|
+
},
|
|
498
|
+
},
|
|
499
|
+
},
|
|
500
|
+
},
|
|
501
|
+
},
|
|
502
|
+
},
|
|
503
|
+
},
|
|
504
|
+
}
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
### Lead Generation
|
|
508
|
+
|
|
509
|
+
```typescript
|
|
510
|
+
{
|
|
511
|
+
mapping: {
|
|
512
|
+
lead: {
|
|
513
|
+
submit: {
|
|
514
|
+
name: 'generate_lead',
|
|
515
|
+
data: {
|
|
516
|
+
map: {
|
|
517
|
+
eventName: { value: 'generate_lead' },
|
|
518
|
+
conversionValue: { value: 10 },
|
|
519
|
+
currency: { value: 'USD' },
|
|
520
|
+
},
|
|
521
|
+
},
|
|
522
|
+
},
|
|
523
|
+
},
|
|
524
|
+
},
|
|
525
|
+
}
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
### Page View (GA4)
|
|
529
|
+
|
|
530
|
+
```typescript
|
|
531
|
+
{
|
|
532
|
+
mapping: {
|
|
533
|
+
page: {
|
|
534
|
+
view: {
|
|
535
|
+
name: 'page_view',
|
|
536
|
+
data: {
|
|
537
|
+
map: {
|
|
538
|
+
eventName: { value: 'page_view' },
|
|
539
|
+
},
|
|
540
|
+
},
|
|
541
|
+
},
|
|
542
|
+
},
|
|
543
|
+
},
|
|
544
|
+
}
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
## Account Types
|
|
548
|
+
|
|
549
|
+
### Google Ads
|
|
550
|
+
|
|
551
|
+
```typescript
|
|
552
|
+
{
|
|
553
|
+
operatingAccount: {
|
|
554
|
+
accountId: '123-456-7890', // Format: XXX-XXX-XXXX
|
|
555
|
+
accountType: 'GOOGLE_ADS',
|
|
556
|
+
},
|
|
557
|
+
productDestinationId: 'AW-CONVERSION-123', // Conversion action ID
|
|
558
|
+
}
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
### Display & Video 360
|
|
562
|
+
|
|
563
|
+
```typescript
|
|
564
|
+
{
|
|
565
|
+
operatingAccount: {
|
|
566
|
+
accountId: '12345', // Advertiser ID
|
|
567
|
+
accountType: 'DISPLAY_VIDEO_ADVERTISER',
|
|
568
|
+
},
|
|
569
|
+
productDestinationId: 'FL-ACTIVITY-123', // Floodlight activity ID
|
|
570
|
+
}
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
### Google Analytics 4
|
|
574
|
+
|
|
575
|
+
```typescript
|
|
576
|
+
{
|
|
577
|
+
operatingAccount: {
|
|
578
|
+
accountId: '123456789', // Property ID
|
|
579
|
+
accountType: 'GOOGLE_ANALYTICS_PROPERTY',
|
|
580
|
+
},
|
|
581
|
+
productDestinationId: 'G-XXXXXXXXXX', // Measurement ID
|
|
582
|
+
}
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
## Data Formatting
|
|
586
|
+
|
|
587
|
+
### Email Normalization
|
|
588
|
+
|
|
589
|
+
- Trim whitespace
|
|
590
|
+
- Convert to lowercase
|
|
591
|
+
- Remove dots (.) for gmail.com/googlemail.com
|
|
592
|
+
- SHA-256 hash
|
|
593
|
+
|
|
594
|
+
**Example:**
|
|
595
|
+
|
|
596
|
+
```
|
|
597
|
+
Input: John.Doe@Gmail.com
|
|
598
|
+
Output: <SHA-256 hash of "johndoe@gmail.com">
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
### Phone Normalization
|
|
602
|
+
|
|
603
|
+
- Convert to E.164 format: `+[country][number]`
|
|
604
|
+
- Remove all non-digit characters except leading +
|
|
605
|
+
- SHA-256 hash
|
|
606
|
+
|
|
607
|
+
**Example:**
|
|
608
|
+
|
|
609
|
+
```
|
|
610
|
+
Input: (800) 555-0100
|
|
611
|
+
Output: <SHA-256 hash of "+18005550100">
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
### Address Formatting
|
|
615
|
+
|
|
616
|
+
- **Names**: Lowercase, remove titles/suffixes, SHA-256 hash
|
|
617
|
+
- **Region Code**: ISO-3166-1 alpha-2, NOT hashed (e.g., "US")
|
|
618
|
+
- **Postal Code**: NOT hashed
|
|
619
|
+
|
|
620
|
+
## Consent Management (DMA)
|
|
621
|
+
|
|
622
|
+
### Required for EEA/UK/Switzerland
|
|
623
|
+
|
|
624
|
+
```typescript
|
|
625
|
+
// Event-level consent
|
|
626
|
+
await elb('order complete', {
|
|
627
|
+
total: 99.99,
|
|
628
|
+
}, {
|
|
629
|
+
consent: {
|
|
630
|
+
marketing: true,
|
|
631
|
+
personalization: false,
|
|
632
|
+
},
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
// Request-level consent (applies to all events)
|
|
636
|
+
{
|
|
637
|
+
settings: {
|
|
638
|
+
consent: {
|
|
639
|
+
adUserData: 'CONSENT_GRANTED',
|
|
640
|
+
adPersonalization: 'CONSENT_GRANTED',
|
|
641
|
+
},
|
|
642
|
+
},
|
|
643
|
+
}
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
## Testing
|
|
647
|
+
|
|
648
|
+
### Validate Mode
|
|
649
|
+
|
|
650
|
+
Test requests without actually ingesting data:
|
|
651
|
+
|
|
652
|
+
```typescript
|
|
653
|
+
{
|
|
654
|
+
settings: {
|
|
655
|
+
validateOnly: true, // Validates structure without ingestion
|
|
656
|
+
testEventCode: 'TEST12345', // For debugging
|
|
657
|
+
},
|
|
658
|
+
}
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
### Test Event Code
|
|
662
|
+
|
|
663
|
+
Add test event code for debugging in production:
|
|
664
|
+
|
|
665
|
+
```typescript
|
|
666
|
+
{
|
|
667
|
+
settings: {
|
|
668
|
+
testEventCode: 'TEST12345',
|
|
669
|
+
},
|
|
670
|
+
}
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
## Debug Mode
|
|
674
|
+
|
|
675
|
+
Enable debug logging to see API requests and responses:
|
|
676
|
+
|
|
677
|
+
```typescript
|
|
678
|
+
{
|
|
679
|
+
settings: {
|
|
680
|
+
logLevel: 'debug', // Shows all API calls and responses
|
|
681
|
+
},
|
|
682
|
+
}
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
**Log levels**: `debug` (all), `info`, `warn`, `error`, `none` (default).
|
|
686
|
+
|
|
687
|
+
Debug mode logs:
|
|
688
|
+
|
|
689
|
+
- Event processing details
|
|
690
|
+
- API request payload and destination count
|
|
691
|
+
- API response status and request ID
|
|
692
|
+
- Validation errors with full context
|
|
693
|
+
|
|
694
|
+
## Deduplication with gtag
|
|
695
|
+
|
|
696
|
+
Prevent double-counting between client-side gtag and server-side Data Manager:
|
|
697
|
+
|
|
698
|
+
### Transaction ID Matching
|
|
699
|
+
|
|
700
|
+
Use the same transaction ID across both platforms:
|
|
701
|
+
|
|
702
|
+
```javascript
|
|
703
|
+
// Client-side gtag
|
|
704
|
+
gtag('event', 'conversion', {
|
|
705
|
+
transaction_id: 'ORDER-123',
|
|
706
|
+
send_to: 'AW-CONVERSION/xxx',
|
|
707
|
+
});
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
```typescript
|
|
711
|
+
// Server-side walkerOS
|
|
712
|
+
await elb('order complete', {
|
|
713
|
+
id: 'ORDER-123', // Must map to transactionId via mapping config
|
|
714
|
+
});
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
Google deduplicates using `transactionId` within 14 days. You must explicitly
|
|
718
|
+
map the transaction ID in your configuration.
|
|
719
|
+
|
|
720
|
+
### GCLID Attribution
|
|
721
|
+
|
|
722
|
+
Map GCLID from your event structure:
|
|
723
|
+
|
|
724
|
+
```typescript
|
|
725
|
+
{
|
|
726
|
+
mapping: {
|
|
727
|
+
order: {
|
|
728
|
+
complete: {
|
|
729
|
+
data: {
|
|
730
|
+
map: {
|
|
731
|
+
gclid: 'context.gclid', // From URL parameter
|
|
732
|
+
transactionId: 'data.id',
|
|
733
|
+
},
|
|
734
|
+
},
|
|
735
|
+
},
|
|
736
|
+
},
|
|
737
|
+
},
|
|
738
|
+
}
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
GCLID is captured by walkerOS browser source from URL parameters (`?gclid=xxx`)
|
|
742
|
+
and stored in `context.gclid`.
|
|
743
|
+
|
|
744
|
+
## Rate Limits
|
|
745
|
+
|
|
746
|
+
- **300 requests per minute** per project
|
|
747
|
+
- **100,000 requests per day** per project
|
|
748
|
+
- Error code: `RESOURCE_EXHAUSTED` (HTTP 429)
|
|
749
|
+
|
|
750
|
+
## Conversion Window
|
|
751
|
+
|
|
752
|
+
**CRITICAL**: Events must occur within the last **14 days**. Older events will
|
|
753
|
+
be rejected.
|
|
754
|
+
|
|
755
|
+
## API Reference
|
|
756
|
+
|
|
757
|
+
### Settings
|
|
758
|
+
|
|
759
|
+
| Property | Type | Required | Description |
|
|
760
|
+
| -------------------------- | -------------- | -------- | -------------------------------------------- |
|
|
761
|
+
| `credentials` | object | \* | Service account (client_email + private_key) |
|
|
762
|
+
| `keyFilename` | string | \* | Path to service account JSON file |
|
|
763
|
+
| `scopes` | string[] | | OAuth scopes (default: datamanager scope) |
|
|
764
|
+
| `destinations` | Destination[] | ✓ | Array of destination accounts (max 10) |
|
|
765
|
+
| `eventSource` | EventSource | | Default event source (WEB, APP, etc.) |
|
|
766
|
+
| `batchSize` | number | | Max events per batch (max 2000) |
|
|
767
|
+
| `batchInterval` | number | | Batch flush interval in ms |
|
|
768
|
+
| `validateOnly` | boolean | | Validate without ingestion |
|
|
769
|
+
| `url` | string | | Override API endpoint |
|
|
770
|
+
| `consent` | Consent | | Request-level consent |
|
|
771
|
+
| `testEventCode` | string | | Test event code for debugging |
|
|
772
|
+
| `userData` | object | | Guided helper: User data mapping |
|
|
773
|
+
| `userId` | string | | Guided helper: First-party user ID |
|
|
774
|
+
| `clientId` | string | | Guided helper: GA4 client ID |
|
|
775
|
+
| `sessionAttributes` | string | | Guided helper: Privacy-safe attribution |
|
|
776
|
+
| `consentAdUserData` | string/boolean | | Consent mapping: Field name or static value |
|
|
777
|
+
| `consentAdPersonalization` | string/boolean | | Consent mapping: Field name or static value |
|
|
778
|
+
|
|
779
|
+
**\* One of `credentials`, `keyFilename`, or ADC (automatic) required for
|
|
780
|
+
authentication**
|
|
781
|
+
|
|
782
|
+
### Event Fields
|
|
783
|
+
|
|
784
|
+
| Field | Type | Max Length | Description |
|
|
785
|
+
| ----------------- | ------ | ---------- | -------------------------------- |
|
|
786
|
+
| `transactionId` | string | 512 | Transaction ID for deduplication |
|
|
787
|
+
| `clientId` | string | 255 | GA client ID |
|
|
788
|
+
| `userId` | string | 256 | First-party user ID |
|
|
789
|
+
| `conversionValue` | number | | Conversion value |
|
|
790
|
+
| `currency` | string | 3 | ISO 4217 currency code |
|
|
791
|
+
| `eventName` | string | 40 | Event name (required for GA4) |
|
|
792
|
+
| `eventSource` | string | | WEB, APP, IN_STORE, PHONE, OTHER |
|
|
793
|
+
|
|
794
|
+
## Type Definitions
|
|
795
|
+
|
|
796
|
+
See [src/types/](./src/types/) for TypeScript interfaces.
|
|
797
|
+
|
|
798
|
+
## Related
|
|
799
|
+
|
|
800
|
+
- [Website Documentation](https://www.walkeros.io/docs/destinations/server/datamanager/)
|
|
801
|
+
- [Destination Interface](../../../core/src/types/destination.ts)
|
|
802
|
+
|
|
803
|
+
## Resources
|
|
804
|
+
|
|
805
|
+
- [Google Data Manager API Documentation](https://developers.google.com/data-manager/api)
|
|
806
|
+
- [Data Formatting Guidelines](https://developers.google.com/data-manager/api/devguides/concepts/formatting)
|
|
807
|
+
- [DMA Compliance](https://developers.google.com/data-manager/api/devguides/concepts/dma)
|
|
808
|
+
- [walkerOS Documentation](https://www.walkeros.io/docs/)
|
|
809
|
+
|
|
810
|
+
## License
|
|
811
|
+
|
|
812
|
+
MIT
|
|
813
|
+
|
|
814
|
+
## Support
|
|
815
|
+
|
|
816
|
+
For issues and questions:
|
|
817
|
+
|
|
818
|
+
- [GitHub Issues](https://github.com/elbwalker/walkerOS/issues)
|
|
819
|
+
- [walkerOS Documentation](https://www.walkeros.io/docs/)
|
|
820
|
+
- [Google Data Manager Support](https://developers.google.com/data-manager/api/support/contact)
|