@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 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)