@datalyr/react-native 1.6.0 → 1.6.2
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 +588 -37
- package/lib/datalyr-sdk.d.ts +13 -1
- package/lib/datalyr-sdk.js +88 -7
- package/lib/http-client.js +1 -1
- package/lib/types.d.ts +6 -2
- package/package.json +1 -1
- package/src/datalyr-sdk-expo.ts +95 -4
- package/src/datalyr-sdk.ts +99 -7
- package/src/http-client.ts +2 -2
- package/src/types.ts +11 -3
package/README.md
CHANGED
|
@@ -12,20 +12,38 @@ Mobile analytics and attribution SDK for React Native and Expo. Track events, id
|
|
|
12
12
|
- [Custom Events](#custom-events)
|
|
13
13
|
- [Screen Views](#screen-views)
|
|
14
14
|
- [E-Commerce Events](#e-commerce-events)
|
|
15
|
+
- [Revenue Events](#revenue-events)
|
|
15
16
|
- [User Identity](#user-identity)
|
|
16
17
|
- [Anonymous ID](#anonymous-id)
|
|
17
18
|
- [Identifying Users](#identifying-users)
|
|
19
|
+
- [Alias](#alias)
|
|
18
20
|
- [User Properties](#user-properties)
|
|
21
|
+
- [Logout](#logout)
|
|
22
|
+
- [Sessions](#sessions)
|
|
19
23
|
- [Attribution](#attribution)
|
|
20
24
|
- [Automatic Capture](#automatic-capture)
|
|
25
|
+
- [Manual Attribution](#manual-attribution)
|
|
21
26
|
- [Web-to-App Attribution](#web-to-app-attribution)
|
|
27
|
+
- [Deferred Attribution](#deferred-attribution)
|
|
28
|
+
- [Customer Journey](#customer-journey)
|
|
22
29
|
- [Event Queue](#event-queue)
|
|
23
30
|
- [Auto Events](#auto-events)
|
|
24
31
|
- [SKAdNetwork](#skadnetwork)
|
|
25
32
|
- [Platform Integrations](#platform-integrations)
|
|
33
|
+
- [Meta (Facebook)](#meta-facebook)
|
|
34
|
+
- [TikTok](#tiktok)
|
|
35
|
+
- [Google Ads](#google-ads)
|
|
26
36
|
- [Apple Search Ads](#apple-search-ads)
|
|
37
|
+
- [Google Play Install Referrer](#google-play-install-referrer)
|
|
38
|
+
- [App Tracking Transparency](#app-tracking-transparency)
|
|
39
|
+
- [Enhanced App Campaigns](#enhanced-app-campaigns)
|
|
40
|
+
- [Third-Party Integrations](#third-party-integrations)
|
|
41
|
+
- [Superwall](#superwall)
|
|
42
|
+
- [RevenueCat](#revenuecat)
|
|
43
|
+
- [Migrating from AppsFlyer / Adjust](#migrating-from-appsflyer--adjust)
|
|
27
44
|
- [Expo Support](#expo-support)
|
|
28
45
|
- [TypeScript](#typescript)
|
|
46
|
+
- [API Reference](#api-reference)
|
|
29
47
|
- [Troubleshooting](#troubleshooting)
|
|
30
48
|
- [License](#license)
|
|
31
49
|
|
|
@@ -118,24 +136,62 @@ Every event includes:
|
|
|
118
136
|
|
|
119
137
|
## Configuration
|
|
120
138
|
|
|
139
|
+
All fields except `apiKey` are optional.
|
|
140
|
+
|
|
121
141
|
```typescript
|
|
122
142
|
await Datalyr.initialize({
|
|
123
143
|
// Required
|
|
124
|
-
apiKey: string,
|
|
144
|
+
apiKey: string, // API key from dashboard (starts with 'dk_')
|
|
145
|
+
|
|
146
|
+
// Optional: workspace
|
|
147
|
+
workspaceId?: string, // Workspace ID for multi-workspace setups
|
|
148
|
+
|
|
149
|
+
// Debugging
|
|
150
|
+
debug?: boolean, // Console logging (default: false)
|
|
151
|
+
|
|
152
|
+
// Network
|
|
153
|
+
endpoint?: string, // API endpoint URL (default: 'https://api.datalyr.com')
|
|
154
|
+
useServerTracking?: boolean, // Use server-side tracking (default: true)
|
|
155
|
+
maxRetries?: number, // Max retry attempts for failed requests (default: 3)
|
|
156
|
+
retryDelay?: number, // Delay between retries in ms (default: 1000)
|
|
157
|
+
timeout?: number, // Request timeout in ms (default: 15000)
|
|
125
158
|
|
|
126
159
|
// Features
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
160
|
+
enableAutoEvents?: boolean, // Track app lifecycle automatically (default: true)
|
|
161
|
+
enableAttribution?: boolean, // Capture attribution data (default: true)
|
|
162
|
+
enableWebToAppAttribution?: boolean, // Web-to-app attribution matching (default: true)
|
|
163
|
+
|
|
164
|
+
// Event queue
|
|
165
|
+
batchSize?: number, // Events per batch (default: 10)
|
|
166
|
+
flushInterval?: number, // Auto-flush interval in ms (default: 30000)
|
|
167
|
+
maxQueueSize?: number, // Max queued events (default: 100)
|
|
130
168
|
|
|
131
|
-
//
|
|
132
|
-
|
|
133
|
-
flushInterval?: number, // Send interval ms (default: 30000)
|
|
134
|
-
maxQueueSize?: number, // Max queued events (default: 100)
|
|
169
|
+
// Auto events
|
|
170
|
+
autoEventConfig?: AutoEventConfig, // Fine-grained auto-event settings (see below)
|
|
135
171
|
|
|
136
172
|
// iOS
|
|
137
|
-
skadTemplate?: 'ecommerce' | 'gaming' | 'subscription',
|
|
173
|
+
skadTemplate?: 'ecommerce' | 'gaming' | 'subscription', // SKAdNetwork conversion template
|
|
174
|
+
});
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### AutoEventConfig
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
interface AutoEventConfig {
|
|
181
|
+
trackSessions?: boolean; // Track session_start / session_end
|
|
182
|
+
trackScreenViews?: boolean; // Track screen views automatically
|
|
183
|
+
trackAppUpdates?: boolean; // Track app_update events
|
|
184
|
+
trackPerformance?: boolean; // Track performance metrics
|
|
185
|
+
sessionTimeoutMs?: number; // Session timeout in ms
|
|
186
|
+
}
|
|
187
|
+
```
|
|
138
188
|
|
|
189
|
+
Update at runtime:
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
Datalyr.updateAutoEventsConfig({
|
|
193
|
+
trackSessions: true,
|
|
194
|
+
sessionTimeoutMs: 1800000, // 30 minutes
|
|
139
195
|
});
|
|
140
196
|
```
|
|
141
197
|
|
|
@@ -145,8 +201,6 @@ await Datalyr.initialize({
|
|
|
145
201
|
|
|
146
202
|
### Custom Events
|
|
147
203
|
|
|
148
|
-
Track any action in your app:
|
|
149
|
-
|
|
150
204
|
```typescript
|
|
151
205
|
// Simple event
|
|
152
206
|
await Datalyr.track('signup_started');
|
|
@@ -170,8 +224,6 @@ await Datalyr.track('level_completed', {
|
|
|
170
224
|
|
|
171
225
|
### Screen Views
|
|
172
226
|
|
|
173
|
-
Track navigation:
|
|
174
|
-
|
|
175
227
|
```typescript
|
|
176
228
|
await Datalyr.screen('Home');
|
|
177
229
|
|
|
@@ -214,6 +266,26 @@ await Datalyr.trackLead(100.0, 'USD');
|
|
|
214
266
|
await Datalyr.trackAddPaymentInfo(true);
|
|
215
267
|
```
|
|
216
268
|
|
|
269
|
+
### Revenue Events
|
|
270
|
+
|
|
271
|
+
Track revenue with automatic SKAdNetwork encoding:
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
await Datalyr.trackRevenue('in_app_purchase', {
|
|
275
|
+
value: 4.99,
|
|
276
|
+
currency: 'USD',
|
|
277
|
+
product_id: 'gems_500',
|
|
278
|
+
});
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### App Update Tracking
|
|
282
|
+
|
|
283
|
+
Manually track version changes:
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
await Datalyr.trackAppUpdate('1.0.0', '1.1.0');
|
|
287
|
+
```
|
|
288
|
+
|
|
217
289
|
---
|
|
218
290
|
|
|
219
291
|
## User Identity
|
|
@@ -246,6 +318,18 @@ After `identify()`:
|
|
|
246
318
|
- All future events include `user_id`
|
|
247
319
|
- Historical anonymous events can be linked server-side
|
|
248
320
|
|
|
321
|
+
### Alias
|
|
322
|
+
|
|
323
|
+
Associate a new user ID with a previous one. Use this when a user's identity changes (e.g., after account merge):
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
// Link new ID to the currently identified user
|
|
327
|
+
await Datalyr.alias('new_user_456');
|
|
328
|
+
|
|
329
|
+
// Or specify the previous ID explicitly
|
|
330
|
+
await Datalyr.alias('new_user_456', 'old_user_123');
|
|
331
|
+
```
|
|
332
|
+
|
|
249
333
|
### User Properties
|
|
250
334
|
|
|
251
335
|
Pass any user attributes:
|
|
@@ -279,6 +363,20 @@ This:
|
|
|
279
363
|
|
|
280
364
|
---
|
|
281
365
|
|
|
366
|
+
## Sessions
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
// Get current session data
|
|
370
|
+
const session = Datalyr.getCurrentSession();
|
|
371
|
+
|
|
372
|
+
// Force end the current session
|
|
373
|
+
await Datalyr.endSession();
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
Sessions are managed automatically when `enableAutoEvents` is enabled. A new session starts on app launch, and the current session ends after 30 minutes of inactivity (configurable via `autoEventConfig.sessionTimeoutMs`).
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
282
380
|
## Attribution
|
|
283
381
|
|
|
284
382
|
### Automatic Capture
|
|
@@ -297,13 +395,24 @@ Captured parameters:
|
|
|
297
395
|
| Click IDs | `fbclid`, `gclid`, `ttclid`, `twclid`, `li_click_id`, `msclkid` |
|
|
298
396
|
| Campaign | `campaign_id`, `adset_id`, `ad_id` |
|
|
299
397
|
|
|
398
|
+
### Manual Attribution
|
|
399
|
+
|
|
400
|
+
Set attribution programmatically:
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
await Datalyr.setAttributionData({
|
|
404
|
+
utm_source: 'newsletter',
|
|
405
|
+
utm_campaign: 'spring_sale',
|
|
406
|
+
});
|
|
407
|
+
```
|
|
408
|
+
|
|
300
409
|
### Web-to-App Attribution
|
|
301
410
|
|
|
302
411
|
Automatically recover attribution from a web prelander when users install the app from an ad.
|
|
303
412
|
|
|
304
413
|
**How it works:**
|
|
305
|
-
- **Android**: Attribution params are passed through the Play Store `referrer` URL parameter (set by the web SDK's `trackAppDownloadClick()`). The mobile SDK reads these via the Play Install Referrer API
|
|
306
|
-
- **iOS**: On first install, the SDK calls the Datalyr API to match the device's IP against recent `$app_download_click` web events within 24 hours
|
|
414
|
+
- **Android**: Attribution params are passed through the Play Store `referrer` URL parameter (set by the web SDK's `trackAppDownloadClick()`). The mobile SDK reads these via the Play Install Referrer API -- deterministic, ~95% accuracy.
|
|
415
|
+
- **iOS**: On first install, the SDK calls the Datalyr API to match the device's IP against recent `$app_download_click` web events within 24 hours -- ~90%+ accuracy for immediate installs.
|
|
307
416
|
|
|
308
417
|
No additional mobile code is needed. Attribution is recovered automatically during `initialize()` on first install, before the `app_install` event fires.
|
|
309
418
|
|
|
@@ -314,15 +423,43 @@ After a match, the SDK:
|
|
|
314
423
|
|
|
315
424
|
**Fallback:** If IP matching misses (e.g., VPN toggle during install), email-based attribution is still recovered when `identify()` is called with the user's email.
|
|
316
425
|
|
|
317
|
-
###
|
|
426
|
+
### Deferred Attribution
|
|
318
427
|
|
|
319
|
-
|
|
428
|
+
Retrieve deferred attribution data captured from deep links or install referrers:
|
|
320
429
|
|
|
321
430
|
```typescript
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
431
|
+
const deferred = Datalyr.getDeferredAttributionData();
|
|
432
|
+
if (deferred) {
|
|
433
|
+
console.log(deferred.url); // Deep link URL
|
|
434
|
+
console.log(deferred.source); // Attribution source
|
|
435
|
+
console.log(deferred.fbclid); // Facebook click ID
|
|
436
|
+
console.log(deferred.gclid); // Google click ID
|
|
437
|
+
console.log(deferred.ttclid); // TikTok click ID
|
|
438
|
+
console.log(deferred.utmSource); // UTM source
|
|
439
|
+
console.log(deferred.utmMedium); // UTM medium
|
|
440
|
+
console.log(deferred.utmCampaign); // UTM campaign
|
|
441
|
+
console.log(deferred.utmContent); // UTM content
|
|
442
|
+
console.log(deferred.utmTerm); // UTM term
|
|
443
|
+
console.log(deferred.campaignId); // Campaign ID
|
|
444
|
+
console.log(deferred.adsetId); // Adset ID
|
|
445
|
+
console.log(deferred.adId); // Ad ID
|
|
446
|
+
}
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
## Customer Journey
|
|
452
|
+
|
|
453
|
+
Access multi-touch attribution journey data via the `datalyr` singleton instance:
|
|
454
|
+
|
|
455
|
+
```typescript
|
|
456
|
+
import { datalyr } from '@datalyr/react-native';
|
|
457
|
+
|
|
458
|
+
// Summary: first/last touch, touchpoint count
|
|
459
|
+
const summary = datalyr.getJourneySummary();
|
|
460
|
+
|
|
461
|
+
// Full journey: all touchpoints in order
|
|
462
|
+
const journey = datalyr.getJourney();
|
|
326
463
|
```
|
|
327
464
|
|
|
328
465
|
---
|
|
@@ -400,7 +537,10 @@ await Datalyr.initialize({
|
|
|
400
537
|
skadTemplate: 'ecommerce',
|
|
401
538
|
});
|
|
402
539
|
|
|
403
|
-
//
|
|
540
|
+
// Track with automatic SKAN conversion value encoding
|
|
541
|
+
await Datalyr.trackWithSKAdNetwork('purchase', { value: 99.99 });
|
|
542
|
+
|
|
543
|
+
// Or use e-commerce helpers (also update SKAN automatically)
|
|
404
544
|
await Datalyr.trackPurchase(99.99, 'USD');
|
|
405
545
|
```
|
|
406
546
|
|
|
@@ -410,11 +550,65 @@ await Datalyr.trackPurchase(99.99, 'USD');
|
|
|
410
550
|
| `gaming` | level_complete, tutorial_complete, purchase, achievement_unlocked |
|
|
411
551
|
| `subscription` | trial_start, subscribe, upgrade, cancel, signup |
|
|
412
552
|
|
|
553
|
+
### Test Conversion Values
|
|
554
|
+
|
|
555
|
+
Preview the conversion value an event would produce without sending it to Apple:
|
|
556
|
+
|
|
557
|
+
```typescript
|
|
558
|
+
const value = Datalyr.getConversionValue('purchase', { value: 49.99 });
|
|
559
|
+
// Returns a number (0-63) or null if no template is configured
|
|
560
|
+
```
|
|
561
|
+
|
|
413
562
|
---
|
|
414
563
|
|
|
415
564
|
## Platform Integrations
|
|
416
565
|
|
|
417
|
-
Conversion
|
|
566
|
+
Conversion events are routed to ad platforms server-side via the Datalyr postback system. No client-side ad SDKs (Facebook SDK, TikTok SDK, etc.) are needed in your app. The SDK captures click IDs and attribution data from ad URLs, then the backend handles hashing, formatting, and sending conversions to each platform's API.
|
|
567
|
+
|
|
568
|
+
### Meta (Facebook)
|
|
569
|
+
|
|
570
|
+
Conversions are sent to Meta via the [Conversions API (CAPI)](https://developers.facebook.com/docs/marketing-api/conversions-api/).
|
|
571
|
+
|
|
572
|
+
**What the SDK does:** Captures `fbclid` from ad click URLs, collects IDFA (when ATT authorized on iOS), and sends user data (email, phone) with events.
|
|
573
|
+
|
|
574
|
+
**What the backend does:** Hashes PII (SHA-256), formats the CAPI payload, and sends conversions with the `fbclid` and `_fbc`/`_fbp` cookies for matching.
|
|
575
|
+
|
|
576
|
+
**Setup:**
|
|
577
|
+
1. Connect your Meta ad account in the Datalyr dashboard (Settings > Connections)
|
|
578
|
+
2. Select your Meta Pixel
|
|
579
|
+
3. Create postback rules to map events (e.g., `purchase` -> `Purchase`, `lead` -> `Lead`)
|
|
580
|
+
|
|
581
|
+
No Facebook SDK needed in your app. No `Info.plist` changes, no `FacebookAppID`.
|
|
582
|
+
|
|
583
|
+
### TikTok
|
|
584
|
+
|
|
585
|
+
Conversions are sent to TikTok via the [Events API](https://business-api.tiktok.com/portal/docs?id=1741601162187777).
|
|
586
|
+
|
|
587
|
+
**What the SDK does:** Captures `ttclid` from ad click URLs and collects device identifiers (IDFA on iOS, GAID on Android).
|
|
588
|
+
|
|
589
|
+
**What the backend does:** Hashes user data, formats the Events API payload, and sends conversions with the `ttclid` and `_ttp` cookie for matching.
|
|
590
|
+
|
|
591
|
+
**Setup:**
|
|
592
|
+
1. Connect your TikTok Ads account in the Datalyr dashboard (Settings > Connections)
|
|
593
|
+
2. Select your TikTok Pixel
|
|
594
|
+
3. Create postback rules to map events (e.g., `purchase` -> `CompletePayment`, `add_to_cart` -> `AddToCart`)
|
|
595
|
+
|
|
596
|
+
No TikTok SDK needed in your app. No access tokens, no native configuration.
|
|
597
|
+
|
|
598
|
+
### Google Ads
|
|
599
|
+
|
|
600
|
+
Conversions are sent to Google via the [Google Ads API](https://developers.google.com/google-ads/api/docs/conversions/overview).
|
|
601
|
+
|
|
602
|
+
**What the SDK does:** Captures `gclid`, `gbraid`, and `wbraid` from ad click URLs. Collects user data for enhanced conversions.
|
|
603
|
+
|
|
604
|
+
**What the backend does:** Hashes user data, maps events to Google conversion actions, and sends conversions with click IDs for attribution.
|
|
605
|
+
|
|
606
|
+
**Setup:**
|
|
607
|
+
1. Connect your Google Ads account in the Datalyr dashboard (Settings > Connections)
|
|
608
|
+
2. Select your conversion actions
|
|
609
|
+
3. Create postback rules to map events (e.g., `purchase` -> your Google conversion action)
|
|
610
|
+
|
|
611
|
+
No Google SDK needed in your app beyond the Play Install Referrer (already included for Android).
|
|
418
612
|
|
|
419
613
|
### Apple Search Ads
|
|
420
614
|
|
|
@@ -442,6 +636,52 @@ Attribution data is automatically included in all events with the `asa_` prefix:
|
|
|
442
636
|
|
|
443
637
|
No additional configuration needed. The SDK uses Apple's AdServices API.
|
|
444
638
|
|
|
639
|
+
### Google Play Install Referrer
|
|
640
|
+
|
|
641
|
+
Android-only. Captures UTM parameters and click IDs from the Google Play Store install referrer URL. This data is retrieved automatically on first launch via the Play Install Referrer API.
|
|
642
|
+
|
|
643
|
+
**How it works:**
|
|
644
|
+
1. User clicks an ad or link with UTM parameters
|
|
645
|
+
2. Google Play Store stores the referrer URL
|
|
646
|
+
3. On first app launch, the SDK retrieves the referrer
|
|
647
|
+
4. Attribution data (utm_source, utm_medium, gclid, etc.) is extracted and merged into the session
|
|
648
|
+
|
|
649
|
+
**Access the raw referrer data:**
|
|
650
|
+
|
|
651
|
+
```typescript
|
|
652
|
+
import { datalyr } from '@datalyr/react-native';
|
|
653
|
+
|
|
654
|
+
const referrer = datalyr.getPlayInstallReferrer();
|
|
655
|
+
if (referrer) {
|
|
656
|
+
// Google Ads click IDs
|
|
657
|
+
console.log(referrer.gclid); // Standard Google Ads click ID
|
|
658
|
+
console.log(referrer.gbraid); // Privacy-safe click ID (iOS App campaigns)
|
|
659
|
+
console.log(referrer.wbraid); // Privacy-safe click ID (Web-to-App campaigns)
|
|
660
|
+
|
|
661
|
+
// UTM parameters
|
|
662
|
+
console.log(referrer.utmSource);
|
|
663
|
+
console.log(referrer.utmMedium);
|
|
664
|
+
console.log(referrer.utmCampaign);
|
|
665
|
+
console.log(referrer.utmTerm);
|
|
666
|
+
console.log(referrer.utmContent);
|
|
667
|
+
|
|
668
|
+
// Timestamps
|
|
669
|
+
console.log(referrer.referrerClickTimestamp); // When the referrer link was clicked (ms)
|
|
670
|
+
console.log(referrer.installBeginTimestamp); // When the install began (ms)
|
|
671
|
+
console.log(referrer.installCompleteTimestamp); // When the install completed (ms)
|
|
672
|
+
|
|
673
|
+
// Raw referrer URL
|
|
674
|
+
console.log(referrer.referrerUrl);
|
|
675
|
+
}
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
**Requirements:**
|
|
679
|
+
- Android only (returns `null` on iOS)
|
|
680
|
+
- Requires the Google Play Install Referrer Library in `android/app/build.gradle`:
|
|
681
|
+
```groovy
|
|
682
|
+
implementation 'com.android.installreferrer:installreferrer:2.2'
|
|
683
|
+
```
|
|
684
|
+
|
|
445
685
|
### App Tracking Transparency
|
|
446
686
|
|
|
447
687
|
Update after ATT dialog:
|
|
@@ -451,37 +691,219 @@ const { status } = await requestTrackingPermissionsAsync();
|
|
|
451
691
|
await Datalyr.updateTrackingAuthorization(status === 'granted');
|
|
452
692
|
```
|
|
453
693
|
|
|
454
|
-
###
|
|
694
|
+
### Integration Status
|
|
455
695
|
|
|
456
696
|
```typescript
|
|
457
697
|
const status = Datalyr.getPlatformIntegrationStatus();
|
|
458
|
-
// { appleSearchAds:
|
|
698
|
+
// { appleSearchAds: boolean, playInstallReferrer: boolean }
|
|
459
699
|
```
|
|
460
700
|
|
|
461
701
|
---
|
|
462
702
|
|
|
463
|
-
##
|
|
703
|
+
## Enhanced App Campaigns
|
|
704
|
+
|
|
705
|
+
Run mobile app ads through web campaigns (Meta Sales, TikTok Traffic, Google Ads) that redirect users to the app store through your own domain. This bypasses SKAN restrictions, ATT requirements, and adset limits -- ad platforms treat these as regular web campaigns.
|
|
706
|
+
|
|
707
|
+
### How It Works
|
|
708
|
+
|
|
709
|
+
1. User clicks your ad -> lands on a page on your domain with the Datalyr web SDK (`dl.js`)
|
|
710
|
+
2. SDK captures attribution (fbclid, ttclid, gclid, UTMs, ad cookies like `_fbp`/`_fbc`/`_ttp`)
|
|
711
|
+
3. User redirects to app store (via button click or auto-redirect)
|
|
712
|
+
4. User installs app -> mobile SDK matches via Play Store referrer (Android, ~95%) or IP matching (iOS, ~90%+)
|
|
713
|
+
5. In-app events fire -> conversions sent to Meta/TikTok/Google server-side via postbacks
|
|
714
|
+
|
|
715
|
+
### Setup
|
|
716
|
+
|
|
717
|
+
**1. Create a tracking link** in the Datalyr dashboard: Track -> Create Link -> App Link. Enter your prelander page URL and app store URLs.
|
|
718
|
+
|
|
719
|
+
**2. Host a page on your domain** with one of these options:
|
|
720
|
+
|
|
721
|
+
#### Option A: Prelander (Recommended)
|
|
722
|
+
|
|
723
|
+
A real landing page with a download button. Better ad platform compliance, higher intent.
|
|
724
|
+
|
|
725
|
+
```html
|
|
726
|
+
<!DOCTYPE html>
|
|
727
|
+
<html>
|
|
728
|
+
<head>
|
|
729
|
+
<meta charset="utf-8">
|
|
730
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
731
|
+
<title>Download Your App</title>
|
|
732
|
+
<script src="https://cdn.datalyr.com/dl.js" data-workspace="YOUR_WORKSPACE_ID"></script>
|
|
733
|
+
</head>
|
|
734
|
+
<body>
|
|
735
|
+
<h1>Download Our App</h1>
|
|
736
|
+
<button id="ios-download">Download for iOS</button>
|
|
737
|
+
<button id="android-download">Download for Android</button>
|
|
738
|
+
|
|
739
|
+
<script>
|
|
740
|
+
document.getElementById('ios-download').addEventListener('click', function() {
|
|
741
|
+
Datalyr.trackAppDownloadClick({
|
|
742
|
+
targetPlatform: 'ios',
|
|
743
|
+
appStoreUrl: 'https://apps.apple.com/app/idXXXXXXXXXX'
|
|
744
|
+
});
|
|
745
|
+
});
|
|
746
|
+
document.getElementById('android-download').addEventListener('click', function() {
|
|
747
|
+
Datalyr.trackAppDownloadClick({
|
|
748
|
+
targetPlatform: 'android',
|
|
749
|
+
appStoreUrl: 'https://play.google.com/store/apps/details?id=com.example.app'
|
|
750
|
+
});
|
|
751
|
+
});
|
|
752
|
+
</script>
|
|
753
|
+
</body>
|
|
754
|
+
</html>
|
|
755
|
+
```
|
|
464
756
|
|
|
465
|
-
|
|
466
|
-
|
|
757
|
+
#### Option B: Redirect Page
|
|
758
|
+
|
|
759
|
+
Instant redirect -- no visible content, user goes straight to app store.
|
|
760
|
+
|
|
761
|
+
> **Note:** Some ad platforms (particularly Meta) may flag redirect pages with no visible content as low-quality landing pages or cloaking. Use the prelander option if compliance is a concern.
|
|
762
|
+
|
|
763
|
+
```html
|
|
764
|
+
<!DOCTYPE html>
|
|
765
|
+
<html>
|
|
766
|
+
<head>
|
|
767
|
+
<script src="https://cdn.datalyr.com/dl.js" data-workspace="YOUR_WORKSPACE_ID"></script>
|
|
768
|
+
<script>
|
|
769
|
+
window.addEventListener('DOMContentLoaded', function() {
|
|
770
|
+
var isAndroid = /android/i.test(navigator.userAgent);
|
|
771
|
+
Datalyr.trackAppDownloadClick({
|
|
772
|
+
targetPlatform: isAndroid ? 'android' : 'ios',
|
|
773
|
+
appStoreUrl: isAndroid
|
|
774
|
+
? 'https://play.google.com/store/apps/details?id=com.example.app'
|
|
775
|
+
: 'https://apps.apple.com/app/idXXXXXXXXXX'
|
|
776
|
+
});
|
|
777
|
+
});
|
|
778
|
+
</script>
|
|
779
|
+
</head>
|
|
780
|
+
<body></body>
|
|
781
|
+
</html>
|
|
467
782
|
```
|
|
468
783
|
|
|
469
|
-
|
|
784
|
+
**3. Set up your ad campaign:**
|
|
785
|
+
|
|
786
|
+
- **Meta Ads**: Campaign objective -> Sales, conversion location -> Website, placements -> Mobile only. Paste your page URL as the Website URL. No SKAN, no ATT, no adset limits.
|
|
787
|
+
- **TikTok Ads**: Campaign objective -> Website Conversions, paste your page URL as destination. Select your TikTok Pixel from Datalyr.
|
|
788
|
+
- **Google Ads**: Performance Max or Search campaign. Use your page URL as the landing page.
|
|
789
|
+
|
|
790
|
+
Add UTM parameters to the URL so attribution flows through:
|
|
791
|
+
- Meta: `?utm_source=facebook&utm_medium=cpc&utm_campaign={{campaign.name}}&utm_content={{adset.name}}&utm_term={{ad.name}}`
|
|
792
|
+
- TikTok: `?utm_source=tiktok&utm_medium=cpc&utm_campaign=__CAMPAIGN_NAME__&utm_content=__AID_NAME__&utm_term=__CID_NAME__`
|
|
793
|
+
- Google: `?utm_source=google&utm_medium=cpc&utm_campaign={campaignid}&utm_content={adgroupid}&utm_term={keyword}`
|
|
794
|
+
|
|
795
|
+
### Important
|
|
796
|
+
|
|
797
|
+
- The page **must load JavaScript**. Server-side redirects (301/302, nginx, Cloudflare Page Rules) will NOT work.
|
|
798
|
+
- Host on your own domain -- do not use `datalyr.com` or shared domains.
|
|
799
|
+
- The redirect page adds ~100-200ms for the SDK to load. Prelander has no latency since the user clicks a button.
|
|
470
800
|
|
|
471
801
|
---
|
|
472
802
|
|
|
473
|
-
##
|
|
803
|
+
## Third-Party Integrations
|
|
804
|
+
|
|
805
|
+
### Superwall
|
|
806
|
+
|
|
807
|
+
Pass Datalyr attribution data to Superwall to personalize paywalls by ad source, campaign, ad set, and keyword.
|
|
474
808
|
|
|
475
809
|
```typescript
|
|
476
|
-
import {
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
810
|
+
import { Datalyr } from '@datalyr/react-native';
|
|
811
|
+
import Superwall from '@superwall/react-native-superwall';
|
|
812
|
+
|
|
813
|
+
// After both SDKs are initialized
|
|
814
|
+
Superwall.setUserAttributes(Datalyr.getSuperwallAttributes());
|
|
815
|
+
|
|
816
|
+
// Your placements will now have attribution data available as filters
|
|
817
|
+
Superwall.register({ placement: 'onboarding_paywall' });
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
Call after `Datalyr.initialize()` completes. If using ATT on iOS, call again after the user responds to the ATT prompt to include the IDFA.
|
|
821
|
+
|
|
822
|
+
**Returned attribute keys:**
|
|
823
|
+
|
|
824
|
+
| Key | Description |
|
|
825
|
+
|-----|-------------|
|
|
826
|
+
| `datalyr_id` | The user's DATALYR visitor ID |
|
|
827
|
+
| `media_source` | Traffic source (e.g., `facebook`, `google`) |
|
|
828
|
+
| `campaign` | Campaign name from the ad |
|
|
829
|
+
| `adgroup` | Ad group or ad set name |
|
|
830
|
+
| `ad` | Individual ad ID |
|
|
831
|
+
| `keyword` | Search keyword that triggered the ad |
|
|
832
|
+
| `network` | Ad network name |
|
|
833
|
+
| `utm_source` | UTM source parameter |
|
|
834
|
+
| `utm_medium` | UTM medium parameter (e.g., `cpc`) |
|
|
835
|
+
| `utm_campaign` | UTM campaign parameter |
|
|
836
|
+
| `utm_term` | UTM term parameter |
|
|
837
|
+
| `utm_content` | UTM content parameter |
|
|
838
|
+
| `lyr` | DATALYR tracking link ID |
|
|
839
|
+
| `fbclid` | Meta click ID from the ad URL |
|
|
840
|
+
| `gclid` | Google click ID from the ad URL |
|
|
841
|
+
| `ttclid` | TikTok click ID from the ad URL |
|
|
842
|
+
| `idfa` | Apple advertising ID (only if ATT authorized) |
|
|
843
|
+
| `gaid` | Google advertising ID (Android) |
|
|
844
|
+
| `att_status` | App Tracking Transparency status (`0`-`3`) |
|
|
845
|
+
|
|
846
|
+
Only non-empty values are included.
|
|
847
|
+
|
|
848
|
+
### RevenueCat
|
|
849
|
+
|
|
850
|
+
Pass Datalyr attribution data to RevenueCat for revenue attribution and offering targeting.
|
|
851
|
+
|
|
852
|
+
```typescript
|
|
853
|
+
import { Datalyr } from '@datalyr/react-native';
|
|
854
|
+
import Purchases from 'react-native-purchases';
|
|
855
|
+
|
|
856
|
+
// After both SDKs are configured
|
|
857
|
+
Purchases.setAttributes(Datalyr.getRevenueCatAttributes());
|
|
483
858
|
```
|
|
484
859
|
|
|
860
|
+
Call after configuring the Purchases SDK and before the first purchase. If using ATT, call again after permission is granted to include IDFA.
|
|
861
|
+
|
|
862
|
+
**Reserved attributes (`$`-prefixed):**
|
|
863
|
+
|
|
864
|
+
| Key | Description |
|
|
865
|
+
|-----|-------------|
|
|
866
|
+
| `$datalyrId` | The user's DATALYR visitor ID |
|
|
867
|
+
| `$mediaSource` | Traffic source (e.g., `facebook`, `google`) |
|
|
868
|
+
| `$campaign` | Campaign name from the ad |
|
|
869
|
+
| `$adGroup` | Ad group or ad set name |
|
|
870
|
+
| `$ad` | Individual ad ID |
|
|
871
|
+
| `$keyword` | Search keyword that triggered the ad |
|
|
872
|
+
| `$idfa` | Apple advertising ID (only if ATT authorized) |
|
|
873
|
+
| `$gpsAdId` | Google advertising ID (Android) |
|
|
874
|
+
| `$attConsentStatus` | ATT consent status (see mapping below) |
|
|
875
|
+
|
|
876
|
+
**ATT status mapping for `$attConsentStatus`:**
|
|
877
|
+
|
|
878
|
+
| ATT Value | String |
|
|
879
|
+
|-----------|--------|
|
|
880
|
+
| 0 | `notDetermined` |
|
|
881
|
+
| 1 | `restricted` |
|
|
882
|
+
| 2 | `denied` |
|
|
883
|
+
| 3 | `authorized` |
|
|
884
|
+
|
|
885
|
+
**Custom attributes:**
|
|
886
|
+
|
|
887
|
+
| Key | Description |
|
|
888
|
+
|-----|-------------|
|
|
889
|
+
| `utm_source` | UTM source parameter |
|
|
890
|
+
| `utm_medium` | UTM medium parameter (e.g., `cpc`) |
|
|
891
|
+
| `utm_campaign` | UTM campaign parameter |
|
|
892
|
+
| `utm_term` | UTM term parameter |
|
|
893
|
+
| `utm_content` | UTM content parameter |
|
|
894
|
+
| `lyr` | DATALYR tracking link ID |
|
|
895
|
+
| `fbclid` | Meta click ID from the ad URL |
|
|
896
|
+
| `gclid` | Google click ID from the ad URL |
|
|
897
|
+
| `ttclid` | TikTok click ID from the ad URL |
|
|
898
|
+
| `wbraid` | Google web-to-app click ID |
|
|
899
|
+
| `gbraid` | Google app click ID |
|
|
900
|
+
| `network` | Ad network |
|
|
901
|
+
| `creative_id` | Creative ID |
|
|
902
|
+
|
|
903
|
+
Only non-empty values are included.
|
|
904
|
+
|
|
905
|
+
> Datalyr also receives Superwall and RevenueCat events via server-side webhooks for analytics. The SDK methods and webhook integration are independent -- you can use one or both.
|
|
906
|
+
|
|
485
907
|
---
|
|
486
908
|
|
|
487
909
|
## Migrating from AppsFlyer / Adjust
|
|
@@ -535,6 +957,135 @@ await Datalyr.trackPurchase(99.99, 'USD');
|
|
|
535
957
|
|
|
536
958
|
---
|
|
537
959
|
|
|
960
|
+
## Expo Support
|
|
961
|
+
|
|
962
|
+
```typescript
|
|
963
|
+
import { Datalyr } from '@datalyr/react-native/expo';
|
|
964
|
+
```
|
|
965
|
+
|
|
966
|
+
Same API as standard React Native.
|
|
967
|
+
|
|
968
|
+
---
|
|
969
|
+
|
|
970
|
+
## TypeScript
|
|
971
|
+
|
|
972
|
+
```typescript
|
|
973
|
+
import {
|
|
974
|
+
Datalyr,
|
|
975
|
+
DatalyrConfig,
|
|
976
|
+
EventData,
|
|
977
|
+
UserProperties,
|
|
978
|
+
AttributionData,
|
|
979
|
+
AutoEventConfig,
|
|
980
|
+
DeferredDeepLinkResult,
|
|
981
|
+
} from '@datalyr/react-native';
|
|
982
|
+
```
|
|
983
|
+
|
|
984
|
+
---
|
|
985
|
+
|
|
986
|
+
## API Reference
|
|
987
|
+
|
|
988
|
+
All methods are static on the `Datalyr` class unless noted otherwise.
|
|
989
|
+
|
|
990
|
+
### Initialization
|
|
991
|
+
|
|
992
|
+
| Method | Description |
|
|
993
|
+
|--------|-------------|
|
|
994
|
+
| `initialize(config: DatalyrConfig)` | Initialize the SDK. Must be called before any other method. |
|
|
995
|
+
|
|
996
|
+
### Event Tracking
|
|
997
|
+
|
|
998
|
+
| Method | Description |
|
|
999
|
+
|--------|-------------|
|
|
1000
|
+
| `track(eventName, eventData?)` | Track a custom event |
|
|
1001
|
+
| `screen(screenName, properties?)` | Track a screen view |
|
|
1002
|
+
| `trackWithSKAdNetwork(event, properties?)` | Track event with SKAN conversion value encoding |
|
|
1003
|
+
| `trackPurchase(value, currency?, productId?)` | Track a purchase |
|
|
1004
|
+
| `trackSubscription(value, currency?, plan?)` | Track a subscription |
|
|
1005
|
+
| `trackRevenue(eventName, properties?)` | Track a revenue event |
|
|
1006
|
+
| `trackAddToCart(value, currency?, productId?, productName?)` | Track add-to-cart |
|
|
1007
|
+
| `trackViewContent(contentId?, contentName?, contentType?, value?, currency?)` | Track content view |
|
|
1008
|
+
| `trackInitiateCheckout(value, currency?, numItems?, productIds?)` | Track checkout start |
|
|
1009
|
+
| `trackCompleteRegistration(method?)` | Track registration |
|
|
1010
|
+
| `trackSearch(query, resultIds?)` | Track a search |
|
|
1011
|
+
| `trackLead(value?, currency?)` | Track a lead |
|
|
1012
|
+
| `trackAddPaymentInfo(success?)` | Track payment info added |
|
|
1013
|
+
| `trackAppUpdate(previousVersion, currentVersion)` | Track an app version update |
|
|
1014
|
+
|
|
1015
|
+
### User Identity
|
|
1016
|
+
|
|
1017
|
+
| Method | Description |
|
|
1018
|
+
|--------|-------------|
|
|
1019
|
+
| `identify(userId, properties?)` | Identify a user |
|
|
1020
|
+
| `alias(newUserId, previousId?)` | Associate a new user ID with a previous one |
|
|
1021
|
+
| `reset()` | Clear user ID and start new session |
|
|
1022
|
+
| `getAnonymousId()` | Get the persistent anonymous device ID |
|
|
1023
|
+
|
|
1024
|
+
### Sessions
|
|
1025
|
+
|
|
1026
|
+
| Method | Description |
|
|
1027
|
+
|--------|-------------|
|
|
1028
|
+
| `getCurrentSession()` | Get current session data |
|
|
1029
|
+
| `endSession()` | Force end the current session |
|
|
1030
|
+
|
|
1031
|
+
### Attribution
|
|
1032
|
+
|
|
1033
|
+
| Method | Description |
|
|
1034
|
+
|--------|-------------|
|
|
1035
|
+
| `getAttributionData()` | Get captured attribution data |
|
|
1036
|
+
| `setAttributionData(data)` | Set attribution data manually |
|
|
1037
|
+
| `getDeferredAttributionData()` | Get deferred attribution from deep links / install referrer |
|
|
1038
|
+
|
|
1039
|
+
### Configuration
|
|
1040
|
+
|
|
1041
|
+
| Method | Description |
|
|
1042
|
+
|--------|-------------|
|
|
1043
|
+
| `updateAutoEventsConfig(config)` | Update auto-event settings at runtime |
|
|
1044
|
+
|
|
1045
|
+
### Platform Integrations
|
|
1046
|
+
|
|
1047
|
+
| Method | Description |
|
|
1048
|
+
|--------|-------------|
|
|
1049
|
+
| `getAppleSearchAdsAttribution()` | Get Apple Search Ads attribution (iOS) |
|
|
1050
|
+
| `getPlatformIntegrationStatus()` | Check which platform integrations are active |
|
|
1051
|
+
| `updateTrackingAuthorization(enabled)` | Update ATT status after user responds to dialog |
|
|
1052
|
+
|
|
1053
|
+
### SKAdNetwork
|
|
1054
|
+
|
|
1055
|
+
| Method | Description |
|
|
1056
|
+
|--------|-------------|
|
|
1057
|
+
| `getConversionValue(event, properties?)` | Preview conversion value without sending to Apple |
|
|
1058
|
+
|
|
1059
|
+
### Third-Party Integrations
|
|
1060
|
+
|
|
1061
|
+
| Method | Description |
|
|
1062
|
+
|--------|-------------|
|
|
1063
|
+
| `getSuperwallAttributes()` | Get attribution formatted for Superwall |
|
|
1064
|
+
| `getRevenueCatAttributes()` | Get attribution formatted for RevenueCat |
|
|
1065
|
+
|
|
1066
|
+
### Status
|
|
1067
|
+
|
|
1068
|
+
| Method | Description |
|
|
1069
|
+
|--------|-------------|
|
|
1070
|
+
| `getStatus()` | Get SDK status (initialized, queue stats, online) |
|
|
1071
|
+
| `flush()` | Send all queued events immediately |
|
|
1072
|
+
|
|
1073
|
+
### Instance Methods (via `datalyr` singleton)
|
|
1074
|
+
|
|
1075
|
+
These methods are available on the `datalyr` instance export, not on the static `Datalyr` class:
|
|
1076
|
+
|
|
1077
|
+
```typescript
|
|
1078
|
+
import { datalyr } from '@datalyr/react-native';
|
|
1079
|
+
```
|
|
1080
|
+
|
|
1081
|
+
| Method | Description |
|
|
1082
|
+
|--------|-------------|
|
|
1083
|
+
| `getJourneySummary()` | Get journey summary (first/last touch, touchpoint count) |
|
|
1084
|
+
| `getJourney()` | Get full customer journey (all touchpoints) |
|
|
1085
|
+
| `getPlayInstallReferrer()` | Get raw Play Install Referrer data (Android) |
|
|
1086
|
+
|
|
1087
|
+
---
|
|
1088
|
+
|
|
538
1089
|
## Troubleshooting
|
|
539
1090
|
|
|
540
1091
|
### Events Not Appearing
|
package/lib/datalyr-sdk.d.ts
CHANGED
|
@@ -176,7 +176,17 @@ export declare class DatalyrSDK {
|
|
|
176
176
|
*/
|
|
177
177
|
getPlayInstallReferrer(): Record<string, any> | null;
|
|
178
178
|
/**
|
|
179
|
-
*
|
|
179
|
+
* Get attribution data formatted for Superwall's setUserAttributes()
|
|
180
|
+
* Returns a flat Record<string, string> with only non-empty values
|
|
181
|
+
*/
|
|
182
|
+
getSuperwallAttributes(): Record<string, string>;
|
|
183
|
+
/**
|
|
184
|
+
* Get attribution data formatted for RevenueCat's Purchases.setAttributes()
|
|
185
|
+
* Returns a flat Record<string, string> with $-prefixed reserved keys
|
|
186
|
+
*/
|
|
187
|
+
getRevenueCatAttributes(): Record<string, string>;
|
|
188
|
+
/**
|
|
189
|
+
* Update tracking authorization status
|
|
180
190
|
* Call this AFTER the user responds to the ATT permission dialog
|
|
181
191
|
*/
|
|
182
192
|
updateTrackingAuthorization(enabled: boolean): Promise<void>;
|
|
@@ -279,5 +289,7 @@ export declare class Datalyr {
|
|
|
279
289
|
};
|
|
280
290
|
static getAppleSearchAdsAttribution(): AppleSearchAdsAttribution | null;
|
|
281
291
|
static updateTrackingAuthorization(enabled: boolean): Promise<void>;
|
|
292
|
+
static getSuperwallAttributes(): Record<string, string>;
|
|
293
|
+
static getRevenueCatAttributes(): Record<string, string>;
|
|
282
294
|
}
|
|
283
295
|
export default datalyr;
|
package/lib/datalyr-sdk.js
CHANGED
|
@@ -23,7 +23,7 @@ export class DatalyrSDK {
|
|
|
23
23
|
workspaceId: '',
|
|
24
24
|
apiKey: '',
|
|
25
25
|
debug: false,
|
|
26
|
-
endpoint: 'https://
|
|
26
|
+
endpoint: 'https://ingest.datalyr.com/track',
|
|
27
27
|
useServerTracking: true, // Default to server-side
|
|
28
28
|
maxRetries: 3,
|
|
29
29
|
retryDelay: 1000,
|
|
@@ -63,7 +63,7 @@ export class DatalyrSDK {
|
|
|
63
63
|
// Set up configuration
|
|
64
64
|
this.state.config = { ...this.state.config, ...config };
|
|
65
65
|
// Initialize HTTP client with server-side API
|
|
66
|
-
this.httpClient = new HttpClient(this.state.config.endpoint || 'https://
|
|
66
|
+
this.httpClient = new HttpClient(this.state.config.endpoint || 'https://ingest.datalyr.com/track', {
|
|
67
67
|
maxRetries: this.state.config.maxRetries || 3,
|
|
68
68
|
retryDelay: this.state.config.retryDelay || 1000,
|
|
69
69
|
timeout: this.state.config.timeout || 15000,
|
|
@@ -177,7 +177,7 @@ export class DatalyrSDK {
|
|
|
177
177
|
const installData = await attributionManager.trackInstall();
|
|
178
178
|
await this.track('app_install', {
|
|
179
179
|
platform: Platform.OS === 'ios' || Platform.OS === 'android' ? Platform.OS : 'android',
|
|
180
|
-
sdk_version: '1.6.
|
|
180
|
+
sdk_version: '1.6.1',
|
|
181
181
|
...installData,
|
|
182
182
|
});
|
|
183
183
|
}
|
|
@@ -347,10 +347,9 @@ export class DatalyrSDK {
|
|
|
347
347
|
}
|
|
348
348
|
try {
|
|
349
349
|
debugLog('Fetching deferred web attribution via IP matching...');
|
|
350
|
-
const baseUrl = this.state.config.endpoint || 'https://api.datalyr.com';
|
|
351
350
|
const controller = new AbortController();
|
|
352
351
|
const timeout = setTimeout(() => controller.abort(), 10000);
|
|
353
|
-
const response = await fetch(
|
|
352
|
+
const response = await fetch('https://api.datalyr.com/attribution/deferred-lookup', {
|
|
354
353
|
method: 'POST',
|
|
355
354
|
headers: {
|
|
356
355
|
'Content-Type': 'application/json',
|
|
@@ -702,8 +701,83 @@ export class DatalyrSDK {
|
|
|
702
701
|
const data = playInstallReferrerIntegration.getReferrerData();
|
|
703
702
|
return data ? playInstallReferrerIntegration.getAttributionData() : null;
|
|
704
703
|
}
|
|
704
|
+
// MARK: - Third-Party Integration Methods
|
|
705
705
|
/**
|
|
706
|
-
*
|
|
706
|
+
* Get attribution data formatted for Superwall's setUserAttributes()
|
|
707
|
+
* Returns a flat Record<string, string> with only non-empty values
|
|
708
|
+
*/
|
|
709
|
+
getSuperwallAttributes() {
|
|
710
|
+
const attribution = attributionManager.getAttributionData();
|
|
711
|
+
const advertiser = this.cachedAdvertiserInfo;
|
|
712
|
+
const attrs = {};
|
|
713
|
+
const set = (key, value) => {
|
|
714
|
+
if (value != null && String(value) !== '')
|
|
715
|
+
attrs[key] = String(value);
|
|
716
|
+
};
|
|
717
|
+
set('datalyr_id', this.state.visitorId);
|
|
718
|
+
set('media_source', attribution.utm_source);
|
|
719
|
+
set('campaign', attribution.utm_campaign);
|
|
720
|
+
set('adgroup', attribution.adset_id || attribution.utm_content);
|
|
721
|
+
set('ad', attribution.ad_id);
|
|
722
|
+
set('keyword', attribution.keyword);
|
|
723
|
+
set('network', attribution.network);
|
|
724
|
+
set('utm_source', attribution.utm_source);
|
|
725
|
+
set('utm_medium', attribution.utm_medium);
|
|
726
|
+
set('utm_campaign', attribution.utm_campaign);
|
|
727
|
+
set('utm_term', attribution.utm_term);
|
|
728
|
+
set('utm_content', attribution.utm_content);
|
|
729
|
+
set('lyr', attribution.lyr);
|
|
730
|
+
set('fbclid', attribution.fbclid);
|
|
731
|
+
set('gclid', attribution.gclid);
|
|
732
|
+
set('ttclid', attribution.ttclid);
|
|
733
|
+
set('idfa', advertiser === null || advertiser === void 0 ? void 0 : advertiser.idfa);
|
|
734
|
+
set('gaid', advertiser === null || advertiser === void 0 ? void 0 : advertiser.gaid);
|
|
735
|
+
set('att_status', advertiser === null || advertiser === void 0 ? void 0 : advertiser.att_status);
|
|
736
|
+
return attrs;
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* Get attribution data formatted for RevenueCat's Purchases.setAttributes()
|
|
740
|
+
* Returns a flat Record<string, string> with $-prefixed reserved keys
|
|
741
|
+
*/
|
|
742
|
+
getRevenueCatAttributes() {
|
|
743
|
+
const attribution = attributionManager.getAttributionData();
|
|
744
|
+
const advertiser = this.cachedAdvertiserInfo;
|
|
745
|
+
const attrs = {};
|
|
746
|
+
const set = (key, value) => {
|
|
747
|
+
if (value != null && String(value) !== '')
|
|
748
|
+
attrs[key] = String(value);
|
|
749
|
+
};
|
|
750
|
+
// Reserved attributes ($ prefix)
|
|
751
|
+
set('$datalyrId', this.state.visitorId);
|
|
752
|
+
set('$mediaSource', attribution.utm_source);
|
|
753
|
+
set('$campaign', attribution.utm_campaign);
|
|
754
|
+
set('$adGroup', attribution.adset_id);
|
|
755
|
+
set('$ad', attribution.ad_id);
|
|
756
|
+
set('$keyword', attribution.keyword);
|
|
757
|
+
set('$idfa', advertiser === null || advertiser === void 0 ? void 0 : advertiser.idfa);
|
|
758
|
+
set('$gpsAdId', advertiser === null || advertiser === void 0 ? void 0 : advertiser.gaid);
|
|
759
|
+
if ((advertiser === null || advertiser === void 0 ? void 0 : advertiser.att_status) != null) {
|
|
760
|
+
const statusMap = { 0: 'notDetermined', 1: 'restricted', 2: 'denied', 3: 'authorized' };
|
|
761
|
+
set('$attConsentStatus', statusMap[advertiser.att_status] || String(advertiser.att_status));
|
|
762
|
+
}
|
|
763
|
+
// Custom attributes
|
|
764
|
+
set('utm_source', attribution.utm_source);
|
|
765
|
+
set('utm_medium', attribution.utm_medium);
|
|
766
|
+
set('utm_campaign', attribution.utm_campaign);
|
|
767
|
+
set('utm_term', attribution.utm_term);
|
|
768
|
+
set('utm_content', attribution.utm_content);
|
|
769
|
+
set('lyr', attribution.lyr);
|
|
770
|
+
set('fbclid', attribution.fbclid);
|
|
771
|
+
set('gclid', attribution.gclid);
|
|
772
|
+
set('ttclid', attribution.ttclid);
|
|
773
|
+
set('wbraid', attribution.wbraid);
|
|
774
|
+
set('gbraid', attribution.gbraid);
|
|
775
|
+
set('network', attribution.network);
|
|
776
|
+
set('creative_id', attribution.creative_id);
|
|
777
|
+
return attrs;
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
780
|
+
* Update tracking authorization status
|
|
707
781
|
* Call this AFTER the user responds to the ATT permission dialog
|
|
708
782
|
*/
|
|
709
783
|
async updateTrackingAuthorization(enabled) {
|
|
@@ -821,7 +895,7 @@ export class DatalyrSDK {
|
|
|
821
895
|
carrier: deviceInfo.carrier,
|
|
822
896
|
network_type: getNetworkType(),
|
|
823
897
|
timestamp: Date.now(),
|
|
824
|
-
sdk_version: '1.6.
|
|
898
|
+
sdk_version: '1.6.1',
|
|
825
899
|
// Advertiser data (IDFA/GAID, ATT status) for server-side postback
|
|
826
900
|
...(advertiserInfo ? {
|
|
827
901
|
idfa: advertiserInfo.idfa,
|
|
@@ -1117,6 +1191,13 @@ export class Datalyr {
|
|
|
1117
1191
|
static async updateTrackingAuthorization(enabled) {
|
|
1118
1192
|
await datalyr.updateTrackingAuthorization(enabled);
|
|
1119
1193
|
}
|
|
1194
|
+
// Third-party integration methods
|
|
1195
|
+
static getSuperwallAttributes() {
|
|
1196
|
+
return datalyr.getSuperwallAttributes();
|
|
1197
|
+
}
|
|
1198
|
+
static getRevenueCatAttributes() {
|
|
1199
|
+
return datalyr.getRevenueCatAttributes();
|
|
1200
|
+
}
|
|
1120
1201
|
}
|
|
1121
1202
|
// Export default instance for backward compatibility
|
|
1122
1203
|
export default datalyr;
|
package/lib/http-client.js
CHANGED
|
@@ -5,7 +5,7 @@ export class HttpClient {
|
|
|
5
5
|
this.requestCount = 0;
|
|
6
6
|
// Use server-side API if flag is set (default to true for v1.0.0)
|
|
7
7
|
this.endpoint = config.useServerTracking !== false
|
|
8
|
-
? 'https://
|
|
8
|
+
? 'https://ingest.datalyr.com/track'
|
|
9
9
|
: endpoint;
|
|
10
10
|
this.config = config;
|
|
11
11
|
}
|
package/lib/types.d.ts
CHANGED
|
@@ -42,11 +42,11 @@ export interface DatalyrConfig {
|
|
|
42
42
|
/** Enable console logging for debugging. Default: false */
|
|
43
43
|
debug?: boolean;
|
|
44
44
|
/**
|
|
45
|
-
* API endpoint URL. Default: 'https://
|
|
45
|
+
* API endpoint URL. Default: 'https://ingest.datalyr.com/track'
|
|
46
46
|
* @deprecated Use `endpoint` instead
|
|
47
47
|
*/
|
|
48
48
|
apiUrl?: string;
|
|
49
|
-
/** API endpoint URL. Default: 'https://
|
|
49
|
+
/** API endpoint URL. Default: 'https://ingest.datalyr.com/track' */
|
|
50
50
|
endpoint?: string;
|
|
51
51
|
/** Use server-side tracking. Default: true */
|
|
52
52
|
useServerTracking?: boolean;
|
|
@@ -187,3 +187,7 @@ export interface AttributionData {
|
|
|
187
187
|
clickId?: string;
|
|
188
188
|
installTime?: string;
|
|
189
189
|
}
|
|
190
|
+
/** Flat dictionary for Superwall's setUserAttributes() */
|
|
191
|
+
export type SuperwallAttributes = Record<string, string>;
|
|
192
|
+
/** Flat dictionary for RevenueCat's Purchases.setAttributes() */
|
|
193
|
+
export type RevenueCatAttributes = Record<string, string>;
|
package/package.json
CHANGED
package/src/datalyr-sdk-expo.ts
CHANGED
|
@@ -55,7 +55,7 @@ export class DatalyrSDKExpo {
|
|
|
55
55
|
workspaceId: '',
|
|
56
56
|
apiKey: '',
|
|
57
57
|
debug: false,
|
|
58
|
-
endpoint: 'https://
|
|
58
|
+
endpoint: 'https://ingest.datalyr.com/track',
|
|
59
59
|
useServerTracking: true,
|
|
60
60
|
maxRetries: 3,
|
|
61
61
|
retryDelay: 1000,
|
|
@@ -92,7 +92,7 @@ export class DatalyrSDKExpo {
|
|
|
92
92
|
|
|
93
93
|
this.state.config = { ...this.state.config, ...config };
|
|
94
94
|
|
|
95
|
-
this.httpClient = new HttpClient(this.state.config.endpoint || 'https://
|
|
95
|
+
this.httpClient = new HttpClient(this.state.config.endpoint || 'https://ingest.datalyr.com/track', {
|
|
96
96
|
maxRetries: this.state.config.maxRetries || 3,
|
|
97
97
|
retryDelay: this.state.config.retryDelay || 1000,
|
|
98
98
|
timeout: this.state.config.timeout || 15000,
|
|
@@ -196,7 +196,7 @@ export class DatalyrSDKExpo {
|
|
|
196
196
|
const installData = await attributionManager.trackInstall();
|
|
197
197
|
await this.track('app_install', {
|
|
198
198
|
platform: Platform.OS,
|
|
199
|
-
sdk_version: '1.6.
|
|
199
|
+
sdk_version: '1.6.1',
|
|
200
200
|
sdk_variant: 'expo',
|
|
201
201
|
...installData,
|
|
202
202
|
});
|
|
@@ -606,6 +606,87 @@ export class DatalyrSDKExpo {
|
|
|
606
606
|
return appleSearchAdsIntegration.getAttributionData();
|
|
607
607
|
}
|
|
608
608
|
|
|
609
|
+
// MARK: - Third-Party Integration Methods
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Get attribution data formatted for Superwall's setUserAttributes()
|
|
613
|
+
*/
|
|
614
|
+
getSuperwallAttributes(): Record<string, string> {
|
|
615
|
+
const attribution = attributionManager.getAttributionData();
|
|
616
|
+
const advertiser = this.cachedAdvertiserInfo;
|
|
617
|
+
const attrs: Record<string, string> = {};
|
|
618
|
+
|
|
619
|
+
const set = (key: string, value: any) => {
|
|
620
|
+
if (value != null && String(value) !== '') attrs[key] = String(value);
|
|
621
|
+
};
|
|
622
|
+
|
|
623
|
+
set('datalyr_id', this.state.visitorId);
|
|
624
|
+
set('media_source', attribution.utm_source);
|
|
625
|
+
set('campaign', attribution.utm_campaign);
|
|
626
|
+
set('adgroup', attribution.adset_id || attribution.utm_content);
|
|
627
|
+
set('ad', attribution.ad_id);
|
|
628
|
+
set('keyword', attribution.keyword);
|
|
629
|
+
set('network', attribution.network);
|
|
630
|
+
set('utm_source', attribution.utm_source);
|
|
631
|
+
set('utm_medium', attribution.utm_medium);
|
|
632
|
+
set('utm_campaign', attribution.utm_campaign);
|
|
633
|
+
set('utm_term', attribution.utm_term);
|
|
634
|
+
set('utm_content', attribution.utm_content);
|
|
635
|
+
set('lyr', attribution.lyr);
|
|
636
|
+
set('fbclid', attribution.fbclid);
|
|
637
|
+
set('gclid', attribution.gclid);
|
|
638
|
+
set('ttclid', attribution.ttclid);
|
|
639
|
+
set('idfa', advertiser?.idfa);
|
|
640
|
+
set('gaid', advertiser?.gaid);
|
|
641
|
+
set('att_status', advertiser?.att_status);
|
|
642
|
+
|
|
643
|
+
return attrs;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Get attribution data formatted for RevenueCat's Purchases.setAttributes()
|
|
648
|
+
*/
|
|
649
|
+
getRevenueCatAttributes(): Record<string, string> {
|
|
650
|
+
const attribution = attributionManager.getAttributionData();
|
|
651
|
+
const advertiser = this.cachedAdvertiserInfo;
|
|
652
|
+
const attrs: Record<string, string> = {};
|
|
653
|
+
|
|
654
|
+
const set = (key: string, value: any) => {
|
|
655
|
+
if (value != null && String(value) !== '') attrs[key] = String(value);
|
|
656
|
+
};
|
|
657
|
+
|
|
658
|
+
// Reserved attributes ($ prefix)
|
|
659
|
+
set('$datalyrId', this.state.visitorId);
|
|
660
|
+
set('$mediaSource', attribution.utm_source);
|
|
661
|
+
set('$campaign', attribution.utm_campaign);
|
|
662
|
+
set('$adGroup', attribution.adset_id);
|
|
663
|
+
set('$ad', attribution.ad_id);
|
|
664
|
+
set('$keyword', attribution.keyword);
|
|
665
|
+
set('$idfa', advertiser?.idfa);
|
|
666
|
+
set('$gpsAdId', advertiser?.gaid);
|
|
667
|
+
if (advertiser?.att_status != null) {
|
|
668
|
+
const statusMap: Record<number, string> = { 0: 'notDetermined', 1: 'restricted', 2: 'denied', 3: 'authorized' };
|
|
669
|
+
set('$attConsentStatus', statusMap[advertiser.att_status] || String(advertiser.att_status));
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// Custom attributes
|
|
673
|
+
set('utm_source', attribution.utm_source);
|
|
674
|
+
set('utm_medium', attribution.utm_medium);
|
|
675
|
+
set('utm_campaign', attribution.utm_campaign);
|
|
676
|
+
set('utm_term', attribution.utm_term);
|
|
677
|
+
set('utm_content', attribution.utm_content);
|
|
678
|
+
set('lyr', attribution.lyr);
|
|
679
|
+
set('fbclid', attribution.fbclid);
|
|
680
|
+
set('gclid', attribution.gclid);
|
|
681
|
+
set('ttclid', attribution.ttclid);
|
|
682
|
+
set('wbraid', attribution.wbraid);
|
|
683
|
+
set('gbraid', attribution.gbraid);
|
|
684
|
+
set('network', attribution.network);
|
|
685
|
+
set('creative_id', attribution.creative_id);
|
|
686
|
+
|
|
687
|
+
return attrs;
|
|
688
|
+
}
|
|
689
|
+
|
|
609
690
|
async updateTrackingAuthorization(authorized: boolean): Promise<void> {
|
|
610
691
|
// Refresh cached advertiser info after ATT status change
|
|
611
692
|
try {
|
|
@@ -700,7 +781,7 @@ export class DatalyrSDKExpo {
|
|
|
700
781
|
carrier: deviceInfo.carrier,
|
|
701
782
|
network_type: networkType,
|
|
702
783
|
timestamp: Date.now(),
|
|
703
|
-
sdk_version: '1.6.
|
|
784
|
+
sdk_version: '1.6.1',
|
|
704
785
|
sdk_variant: 'expo',
|
|
705
786
|
// Advertiser data (IDFA/GAID, ATT status) for server-side postback
|
|
706
787
|
...(advertiserInfo ? {
|
|
@@ -960,6 +1041,16 @@ export class DatalyrExpo {
|
|
|
960
1041
|
static async updateTrackingAuthorization(authorized: boolean): Promise<void> {
|
|
961
1042
|
await datalyrExpo.updateTrackingAuthorization(authorized);
|
|
962
1043
|
}
|
|
1044
|
+
|
|
1045
|
+
// Third-party integration methods
|
|
1046
|
+
|
|
1047
|
+
static getSuperwallAttributes(): Record<string, string> {
|
|
1048
|
+
return datalyrExpo.getSuperwallAttributes();
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
static getRevenueCatAttributes(): Record<string, string> {
|
|
1052
|
+
return datalyrExpo.getRevenueCatAttributes();
|
|
1053
|
+
}
|
|
963
1054
|
}
|
|
964
1055
|
|
|
965
1056
|
export default datalyrExpo;
|
package/src/datalyr-sdk.ts
CHANGED
|
@@ -54,7 +54,7 @@ export class DatalyrSDK {
|
|
|
54
54
|
workspaceId: '',
|
|
55
55
|
apiKey: '',
|
|
56
56
|
debug: false,
|
|
57
|
-
endpoint: 'https://
|
|
57
|
+
endpoint: 'https://ingest.datalyr.com/track',
|
|
58
58
|
useServerTracking: true, // Default to server-side
|
|
59
59
|
maxRetries: 3,
|
|
60
60
|
retryDelay: 1000,
|
|
@@ -99,7 +99,7 @@ export class DatalyrSDK {
|
|
|
99
99
|
this.state.config = { ...this.state.config, ...config };
|
|
100
100
|
|
|
101
101
|
// Initialize HTTP client with server-side API
|
|
102
|
-
this.httpClient = new HttpClient(this.state.config.endpoint || 'https://
|
|
102
|
+
this.httpClient = new HttpClient(this.state.config.endpoint || 'https://ingest.datalyr.com/track', {
|
|
103
103
|
maxRetries: this.state.config.maxRetries || 3,
|
|
104
104
|
retryDelay: this.state.config.retryDelay || 1000,
|
|
105
105
|
timeout: this.state.config.timeout || 15000,
|
|
@@ -228,7 +228,7 @@ export class DatalyrSDK {
|
|
|
228
228
|
const installData = await attributionManager.trackInstall();
|
|
229
229
|
await this.track('app_install', {
|
|
230
230
|
platform: Platform.OS === 'ios' || Platform.OS === 'android' ? Platform.OS : 'android',
|
|
231
|
-
sdk_version: '1.6.
|
|
231
|
+
sdk_version: '1.6.1',
|
|
232
232
|
...installData,
|
|
233
233
|
});
|
|
234
234
|
}
|
|
@@ -425,11 +425,10 @@ export class DatalyrSDK {
|
|
|
425
425
|
try {
|
|
426
426
|
debugLog('Fetching deferred web attribution via IP matching...');
|
|
427
427
|
|
|
428
|
-
const baseUrl = this.state.config.endpoint || 'https://api.datalyr.com';
|
|
429
428
|
const controller = new AbortController();
|
|
430
429
|
const timeout = setTimeout(() => controller.abort(), 10000);
|
|
431
430
|
|
|
432
|
-
const response = await fetch(
|
|
431
|
+
const response = await fetch('https://api.datalyr.com/attribution/deferred-lookup', {
|
|
433
432
|
method: 'POST',
|
|
434
433
|
headers: {
|
|
435
434
|
'Content-Type': 'application/json',
|
|
@@ -862,8 +861,91 @@ export class DatalyrSDK {
|
|
|
862
861
|
return data ? playInstallReferrerIntegration.getAttributionData() : null;
|
|
863
862
|
}
|
|
864
863
|
|
|
864
|
+
// MARK: - Third-Party Integration Methods
|
|
865
|
+
|
|
865
866
|
/**
|
|
866
|
-
*
|
|
867
|
+
* Get attribution data formatted for Superwall's setUserAttributes()
|
|
868
|
+
* Returns a flat Record<string, string> with only non-empty values
|
|
869
|
+
*/
|
|
870
|
+
getSuperwallAttributes(): Record<string, string> {
|
|
871
|
+
const attribution = attributionManager.getAttributionData();
|
|
872
|
+
const advertiser = this.cachedAdvertiserInfo;
|
|
873
|
+
const attrs: Record<string, string> = {};
|
|
874
|
+
|
|
875
|
+
const set = (key: string, value: any) => {
|
|
876
|
+
if (value != null && String(value) !== '') attrs[key] = String(value);
|
|
877
|
+
};
|
|
878
|
+
|
|
879
|
+
set('datalyr_id', this.state.visitorId);
|
|
880
|
+
set('media_source', attribution.utm_source);
|
|
881
|
+
set('campaign', attribution.utm_campaign);
|
|
882
|
+
set('adgroup', attribution.adset_id || attribution.utm_content);
|
|
883
|
+
set('ad', attribution.ad_id);
|
|
884
|
+
set('keyword', attribution.keyword);
|
|
885
|
+
set('network', attribution.network);
|
|
886
|
+
set('utm_source', attribution.utm_source);
|
|
887
|
+
set('utm_medium', attribution.utm_medium);
|
|
888
|
+
set('utm_campaign', attribution.utm_campaign);
|
|
889
|
+
set('utm_term', attribution.utm_term);
|
|
890
|
+
set('utm_content', attribution.utm_content);
|
|
891
|
+
set('lyr', attribution.lyr);
|
|
892
|
+
set('fbclid', attribution.fbclid);
|
|
893
|
+
set('gclid', attribution.gclid);
|
|
894
|
+
set('ttclid', attribution.ttclid);
|
|
895
|
+
set('idfa', advertiser?.idfa);
|
|
896
|
+
set('gaid', advertiser?.gaid);
|
|
897
|
+
set('att_status', advertiser?.att_status);
|
|
898
|
+
|
|
899
|
+
return attrs;
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
/**
|
|
903
|
+
* Get attribution data formatted for RevenueCat's Purchases.setAttributes()
|
|
904
|
+
* Returns a flat Record<string, string> with $-prefixed reserved keys
|
|
905
|
+
*/
|
|
906
|
+
getRevenueCatAttributes(): Record<string, string> {
|
|
907
|
+
const attribution = attributionManager.getAttributionData();
|
|
908
|
+
const advertiser = this.cachedAdvertiserInfo;
|
|
909
|
+
const attrs: Record<string, string> = {};
|
|
910
|
+
|
|
911
|
+
const set = (key: string, value: any) => {
|
|
912
|
+
if (value != null && String(value) !== '') attrs[key] = String(value);
|
|
913
|
+
};
|
|
914
|
+
|
|
915
|
+
// Reserved attributes ($ prefix)
|
|
916
|
+
set('$datalyrId', this.state.visitorId);
|
|
917
|
+
set('$mediaSource', attribution.utm_source);
|
|
918
|
+
set('$campaign', attribution.utm_campaign);
|
|
919
|
+
set('$adGroup', attribution.adset_id);
|
|
920
|
+
set('$ad', attribution.ad_id);
|
|
921
|
+
set('$keyword', attribution.keyword);
|
|
922
|
+
set('$idfa', advertiser?.idfa);
|
|
923
|
+
set('$gpsAdId', advertiser?.gaid);
|
|
924
|
+
if (advertiser?.att_status != null) {
|
|
925
|
+
const statusMap: Record<number, string> = { 0: 'notDetermined', 1: 'restricted', 2: 'denied', 3: 'authorized' };
|
|
926
|
+
set('$attConsentStatus', statusMap[advertiser.att_status] || String(advertiser.att_status));
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
// Custom attributes
|
|
930
|
+
set('utm_source', attribution.utm_source);
|
|
931
|
+
set('utm_medium', attribution.utm_medium);
|
|
932
|
+
set('utm_campaign', attribution.utm_campaign);
|
|
933
|
+
set('utm_term', attribution.utm_term);
|
|
934
|
+
set('utm_content', attribution.utm_content);
|
|
935
|
+
set('lyr', attribution.lyr);
|
|
936
|
+
set('fbclid', attribution.fbclid);
|
|
937
|
+
set('gclid', attribution.gclid);
|
|
938
|
+
set('ttclid', attribution.ttclid);
|
|
939
|
+
set('wbraid', attribution.wbraid);
|
|
940
|
+
set('gbraid', attribution.gbraid);
|
|
941
|
+
set('network', attribution.network);
|
|
942
|
+
set('creative_id', attribution.creative_id);
|
|
943
|
+
|
|
944
|
+
return attrs;
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
/**
|
|
948
|
+
* Update tracking authorization status
|
|
867
949
|
* Call this AFTER the user responds to the ATT permission dialog
|
|
868
950
|
*/
|
|
869
951
|
async updateTrackingAuthorization(enabled: boolean): Promise<void> {
|
|
@@ -991,7 +1073,7 @@ export class DatalyrSDK {
|
|
|
991
1073
|
carrier: deviceInfo.carrier,
|
|
992
1074
|
network_type: getNetworkType(),
|
|
993
1075
|
timestamp: Date.now(),
|
|
994
|
-
sdk_version: '1.6.
|
|
1076
|
+
sdk_version: '1.6.1',
|
|
995
1077
|
// Advertiser data (IDFA/GAID, ATT status) for server-side postback
|
|
996
1078
|
...(advertiserInfo ? {
|
|
997
1079
|
idfa: advertiserInfo.idfa,
|
|
@@ -1364,6 +1446,16 @@ export class Datalyr {
|
|
|
1364
1446
|
static async updateTrackingAuthorization(enabled: boolean): Promise<void> {
|
|
1365
1447
|
await datalyr.updateTrackingAuthorization(enabled);
|
|
1366
1448
|
}
|
|
1449
|
+
|
|
1450
|
+
// Third-party integration methods
|
|
1451
|
+
|
|
1452
|
+
static getSuperwallAttributes(): Record<string, string> {
|
|
1453
|
+
return datalyr.getSuperwallAttributes();
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
static getRevenueCatAttributes(): Record<string, string> {
|
|
1457
|
+
return datalyr.getRevenueCatAttributes();
|
|
1458
|
+
}
|
|
1367
1459
|
}
|
|
1368
1460
|
|
|
1369
1461
|
// Export default instance for backward compatibility
|
package/src/http-client.ts
CHANGED
|
@@ -26,8 +26,8 @@ export class HttpClient {
|
|
|
26
26
|
|
|
27
27
|
constructor(endpoint: string, config: HttpClientConfig) {
|
|
28
28
|
// Use server-side API if flag is set (default to true for v1.0.0)
|
|
29
|
-
this.endpoint = config.useServerTracking !== false
|
|
30
|
-
? 'https://
|
|
29
|
+
this.endpoint = config.useServerTracking !== false
|
|
30
|
+
? 'https://ingest.datalyr.com/track'
|
|
31
31
|
: endpoint;
|
|
32
32
|
this.config = config;
|
|
33
33
|
}
|
package/src/types.ts
CHANGED
|
@@ -49,12 +49,12 @@ export interface DatalyrConfig {
|
|
|
49
49
|
debug?: boolean;
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
|
-
* API endpoint URL. Default: 'https://
|
|
52
|
+
* API endpoint URL. Default: 'https://ingest.datalyr.com/track'
|
|
53
53
|
* @deprecated Use `endpoint` instead
|
|
54
54
|
*/
|
|
55
55
|
apiUrl?: string;
|
|
56
56
|
|
|
57
|
-
/** API endpoint URL. Default: 'https://
|
|
57
|
+
/** API endpoint URL. Default: 'https://ingest.datalyr.com/track' */
|
|
58
58
|
endpoint?: string;
|
|
59
59
|
|
|
60
60
|
/** Use server-side tracking. Default: true */
|
|
@@ -227,4 +227,12 @@ export interface AttributionData {
|
|
|
227
227
|
content?: string;
|
|
228
228
|
clickId?: string;
|
|
229
229
|
installTime?: string;
|
|
230
|
-
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Third-Party Integration Attributes
|
|
233
|
+
|
|
234
|
+
/** Flat dictionary for Superwall's setUserAttributes() */
|
|
235
|
+
export type SuperwallAttributes = Record<string, string>;
|
|
236
|
+
|
|
237
|
+
/** Flat dictionary for RevenueCat's Purchases.setAttributes() */
|
|
238
|
+
export type RevenueCatAttributes = Record<string, string>;
|