@proveanything/smartlinks 1.7.9 → 1.8.0
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 +3 -0
- package/dist/api/analytics.d.ts +57 -0
- package/dist/api/analytics.js +542 -0
- package/dist/api/auth.d.ts +23 -35
- package/dist/api/auth.js +104 -0
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.js +1 -0
- package/dist/api/order.d.ts +14 -5
- package/dist/api/order.js +19 -8
- package/dist/docs/API_SUMMARY.md +595 -64
- package/dist/docs/analytics-metadata-conventions.md +80 -0
- package/dist/docs/analytics.md +690 -0
- package/dist/docs/interactions.md +2 -0
- package/dist/docs/overview.md +2 -1
- package/dist/index.d.ts +1 -1
- package/dist/openapi.yaml +853 -115
- package/dist/types/analytics.d.ts +307 -0
- package/dist/types/analytics.js +7 -1
- package/dist/types/auth.d.ts +67 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/order.d.ts +25 -28
- package/docs/API_SUMMARY.md +595 -64
- package/docs/analytics-metadata-conventions.md +80 -0
- package/docs/analytics.md +690 -0
- package/docs/interactions.md +2 -0
- package/docs/overview.md +2 -1
- package/openapi.yaml +853 -115
- package/package.json +1 -1
|
@@ -0,0 +1,690 @@
|
|
|
1
|
+
# Collection Analytics
|
|
2
|
+
|
|
3
|
+
Build collection analytics dashboards and fire-and-forget tracking with the SmartLinks SDK.
|
|
4
|
+
|
|
5
|
+
This guide covers the `analytics` namespace, which is separate from `interactions`:
|
|
6
|
+
|
|
7
|
+
- Use `analytics` for generic web analytics, link clicks, QR landing pages, and tag scan telemetry
|
|
8
|
+
- Use `interactions` for structured user actions like votes, entries, submissions, and journey-triggering events
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Start Here
|
|
13
|
+
|
|
14
|
+
### Use `analytics` when you want to...
|
|
15
|
+
|
|
16
|
+
- Track page views without waiting for a response
|
|
17
|
+
- Log outbound or internal link clicks
|
|
18
|
+
- Capture NFC / QR scan telemetry
|
|
19
|
+
- Build web analytics dashboards for a collection
|
|
20
|
+
- Query raw analytics events, time series, or grouped breakdowns
|
|
21
|
+
|
|
22
|
+
### Use `interactions` when you want to...
|
|
23
|
+
|
|
24
|
+
- Define named interaction types per collection
|
|
25
|
+
- Count outcomes like votes or registrations
|
|
26
|
+
- Track user actions with explicit business meaning
|
|
27
|
+
- Feed journeys, competitions, or participation logic
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Overview
|
|
32
|
+
|
|
33
|
+
The `analytics` namespace has two layers:
|
|
34
|
+
|
|
35
|
+
| Layer | Purpose |
|
|
36
|
+
|---|---|
|
|
37
|
+
| Public ingestion | Fire-and-forget event logging from the browser or client runtime |
|
|
38
|
+
| Admin queries | Dashboard and reporting APIs for collection analytics |
|
|
39
|
+
|
|
40
|
+
There are two analytics domains:
|
|
41
|
+
|
|
42
|
+
| Domain | Route | Typical use |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| Collection events | `/public/analytics/collection` | Page views, clicks, app navigation, landing pages |
|
|
45
|
+
| Tag events | `/public/analytics/tag` | NFC / QR scans, claim/code activity, suspicious scan monitoring |
|
|
46
|
+
|
|
47
|
+
The backend stores custom analytics dimensions in `metadata`. For the most common attribution and placement keys, the public ingestion endpoints also accept standard top-level convenience fields and mirror them into `metadata` automatically.
|
|
48
|
+
|
|
49
|
+
See [docs/analytics-metadata-conventions.md](analytics-metadata-conventions.md) for the recommended key set.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Quick Start
|
|
54
|
+
|
|
55
|
+
### Track a page view
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
import { initializeApi, analytics } from '@proveanything/smartlinks'
|
|
59
|
+
|
|
60
|
+
initializeApi({ baseURL: 'https://smartlinks.app/api/v1' })
|
|
61
|
+
|
|
62
|
+
analytics.collection.track({
|
|
63
|
+
sessionId: 'sess_123',
|
|
64
|
+
eventType: 'page_view',
|
|
65
|
+
collectionId: 'demo-collection',
|
|
66
|
+
productId: 'product_1',
|
|
67
|
+
appId: 'homepage',
|
|
68
|
+
path: '/c/demo-collection',
|
|
69
|
+
deviceType: 'mobile',
|
|
70
|
+
})
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Track an outbound click
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
analytics.collection.track({
|
|
77
|
+
sessionId: 'sess_123',
|
|
78
|
+
eventType: 'click_link',
|
|
79
|
+
collectionId: 'demo-collection',
|
|
80
|
+
productId: 'product_1',
|
|
81
|
+
linkId: 'hero-cta',
|
|
82
|
+
destinationAppId: 'shop',
|
|
83
|
+
href: 'https://example.com/buy',
|
|
84
|
+
isExternal: true,
|
|
85
|
+
placement: 'hero',
|
|
86
|
+
campaign: 'summer-launch',
|
|
87
|
+
})
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Track a tag scan
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
analytics.tag.track({
|
|
94
|
+
sessionId: 'sess_123',
|
|
95
|
+
eventType: 'scan_tag',
|
|
96
|
+
collectionId: 'demo-collection',
|
|
97
|
+
productId: 'product_1',
|
|
98
|
+
codeId: 'code_123',
|
|
99
|
+
claimId: 'claim_456',
|
|
100
|
+
path: '/claim/code_123',
|
|
101
|
+
isAdmin: false,
|
|
102
|
+
location: { country: 'US' },
|
|
103
|
+
entryType: 'nfc',
|
|
104
|
+
scanMethod: 'nfc',
|
|
105
|
+
})
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Fire-and-Forget Tracking
|
|
111
|
+
|
|
112
|
+
`analytics.collection.track(...)` and `analytics.tag.track(...)` are designed for browser-friendly event logging.
|
|
113
|
+
|
|
114
|
+
They use this strategy:
|
|
115
|
+
|
|
116
|
+
1. `navigator.sendBeacon()` when available
|
|
117
|
+
2. fallback to `fetch(..., { keepalive: true })`
|
|
118
|
+
3. do not wait for a response before continuing
|
|
119
|
+
|
|
120
|
+
This makes them a good fit for:
|
|
121
|
+
|
|
122
|
+
- page unload events
|
|
123
|
+
- click logging before navigation
|
|
124
|
+
- low-friction telemetry
|
|
125
|
+
- QR / NFC landing-page analytics
|
|
126
|
+
|
|
127
|
+
### Return value
|
|
128
|
+
|
|
129
|
+
Both tracking helpers return a small synchronous result:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
const result = analytics.collection.track({
|
|
133
|
+
eventType: 'page_view',
|
|
134
|
+
collectionId: 'demo-collection',
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
console.log(result)
|
|
138
|
+
// { queued: true, transport: 'beacon' }
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
If you need a fully awaited request/response flow for business logic, use a regular admin or public API instead of fire-and-forget analytics tracking.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Browser Helpers
|
|
146
|
+
|
|
147
|
+
If you are replacing older portal-style tracking code, the SDK now includes a small browser helper layer under `analytics.browser`.
|
|
148
|
+
|
|
149
|
+
These helpers are useful when you want the SDK to fill in common defaults such as:
|
|
150
|
+
|
|
151
|
+
- `sessionId`
|
|
152
|
+
- `visitorId`
|
|
153
|
+
- `deviceType`
|
|
154
|
+
- current `path`
|
|
155
|
+
- `pagePath`
|
|
156
|
+
- `referrer` and `referrerHost`
|
|
157
|
+
- campaign and query fields like `utmSource`, `utmCampaign`, `pageId`, and `qrCodeId`
|
|
158
|
+
- cached geolocation when you opt into it
|
|
159
|
+
|
|
160
|
+
Example:
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
analytics.browser.configure({
|
|
164
|
+
visitorId: 'shared-visitor-id',
|
|
165
|
+
defaultCollectionEvent: {
|
|
166
|
+
collectionId: 'demo-collection',
|
|
167
|
+
productId: 'product_1',
|
|
168
|
+
},
|
|
169
|
+
getCollectionDefaults: () => ({
|
|
170
|
+
appId: 'homepage',
|
|
171
|
+
}),
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
analytics.browser.trackPageView({
|
|
175
|
+
placement: 'hero',
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
analytics.browser.trackLinkClick({
|
|
179
|
+
href: 'https://example.com/buy',
|
|
180
|
+
linkId: 'hero-cta',
|
|
181
|
+
destinationAppId: 'shop',
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
console.log(analytics.browser.getVisitorId())
|
|
185
|
+
|
|
186
|
+
analytics.browser.setVisitorId('shared-visitor-id')
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
If your apps and widgets run on the same domain, `visitorId` can be shared through browser storage. That means an outer shell can explicitly seed the ID once with either `analytics.browser.configure({ visitorId })` or `analytics.browser.setVisitorId(...)`, and inner apps can reuse it through `analytics.browser.getVisitorId()`.
|
|
190
|
+
|
|
191
|
+
### Campaign and query capture
|
|
192
|
+
|
|
193
|
+
Browser helpers automatically capture common analytics query parameters such as:
|
|
194
|
+
|
|
195
|
+
- `utm_source`, `utm_medium`, `utm_campaign`, `utm_content`, `utm_term`
|
|
196
|
+
- `pageId`, `qrCodeId`
|
|
197
|
+
- `group`, `tag`, `campaign`, `placement`
|
|
198
|
+
|
|
199
|
+
You can also read them directly when building your own event shape:
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
const campaignFields = analytics.browser.captureCampaignParams()
|
|
203
|
+
|
|
204
|
+
analytics.browser.trackPageView({
|
|
205
|
+
collectionId: 'demo-collection',
|
|
206
|
+
productId: 'product_1',
|
|
207
|
+
...campaignFields,
|
|
208
|
+
})
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### SPA page-view tracking
|
|
212
|
+
|
|
213
|
+
For single-page apps, use `bindPageViews()` once during app startup to automatically track route changes.
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
const stopPageTracking = analytics.browser.bindPageViews({
|
|
217
|
+
trackInitialPageView: true,
|
|
218
|
+
event: {
|
|
219
|
+
collectionId: 'demo-collection',
|
|
220
|
+
productId: 'product_1',
|
|
221
|
+
appId: 'homepage',
|
|
222
|
+
},
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
// Later, if needed:
|
|
226
|
+
stopPageTracking()
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Delegated link tracking
|
|
230
|
+
|
|
231
|
+
For classic click analytics, use `bindLinkTracking()` instead of wiring individual anchors one by one.
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
const stopLinkTracking = analytics.browser.bindLinkTracking({
|
|
235
|
+
event: {
|
|
236
|
+
collectionId: 'demo-collection',
|
|
237
|
+
productId: 'product_1',
|
|
238
|
+
appId: 'homepage',
|
|
239
|
+
},
|
|
240
|
+
trackInternal: true,
|
|
241
|
+
})
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Opt-in geolocation
|
|
245
|
+
|
|
246
|
+
Automatic geolocation should stay opt-in because it depends on browser permission, product UX, and privacy requirements.
|
|
247
|
+
|
|
248
|
+
If you only need coarse regional data such as country, prefer the auth location endpoints instead of prompting for browser geolocation:
|
|
249
|
+
|
|
250
|
+
- use `auth.getAccount()` and read `account.location` when the user is authenticated
|
|
251
|
+
- use `auth.getLocation()` when the user is anonymous and you want best-effort IP-derived country/location data
|
|
252
|
+
|
|
253
|
+
`auth.getLocation()` is a good candidate for session caching. The SDK now caches it in session storage by default for 30 minutes, and you can tune or disable that behavior if needed.
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
const account = hasAuthCredentials()
|
|
257
|
+
? await auth.getAccount()
|
|
258
|
+
: null
|
|
259
|
+
|
|
260
|
+
const location = account?.location ?? await auth.getLocation({
|
|
261
|
+
ttlMs: 30 * 60 * 1000,
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
analytics.browser.setLocation(location)
|
|
265
|
+
|
|
266
|
+
analytics.browser.trackTagScan({
|
|
267
|
+
collectionId: 'demo-collection',
|
|
268
|
+
productId: 'product_1',
|
|
269
|
+
codeId: 'code_123',
|
|
270
|
+
})
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
If you later need to refresh that coarse location explicitly, call `auth.getLocation({ forceRefresh: true })` or clear it first with `auth.clearCachedLocation()`.
|
|
274
|
+
|
|
275
|
+
Use browser geolocation only when you actually need precise coordinates:
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
await analytics.browser.captureLocation({
|
|
279
|
+
enableHighAccuracy: false,
|
|
280
|
+
timeout: 3000,
|
|
281
|
+
includeCoordinates: true,
|
|
282
|
+
})
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
If your app already has a trusted location source, you can set it directly with `analytics.browser.setLocation(...)` instead of asking the browser again.
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## Public Ingestion
|
|
290
|
+
|
|
291
|
+
### `analytics.collection.track(event)`
|
|
292
|
+
|
|
293
|
+
Tracks generic collection analytics events such as:
|
|
294
|
+
|
|
295
|
+
- `page_view`
|
|
296
|
+
- `click_link`
|
|
297
|
+
- QR landing-page visits
|
|
298
|
+
- internal navigation
|
|
299
|
+
- outbound link activity
|
|
300
|
+
|
|
301
|
+
Supported top-level fields include the core event fields plus standard convenience metadata fields such as `referrer`, `utmSource`, `group`, `placement`, `linkTitle`, `pagePath`, and `qrCodeId`.
|
|
302
|
+
|
|
303
|
+
`visitorId` is also supported as a standard top-level field and is mirrored into `metadata` by the backend for backward compatibility.
|
|
304
|
+
|
|
305
|
+
Example:
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
analytics.collection.track({
|
|
309
|
+
sessionId: 'sess_123',
|
|
310
|
+
eventType: 'page_view',
|
|
311
|
+
collectionId: 'demo-collection',
|
|
312
|
+
productId: 'product_1',
|
|
313
|
+
appId: 'homepage',
|
|
314
|
+
destinationAppId: 'shop',
|
|
315
|
+
linkId: 'hero-cta',
|
|
316
|
+
deviceType: 'mobile',
|
|
317
|
+
href: 'https://example.com/buy',
|
|
318
|
+
path: '/c/demo-collection',
|
|
319
|
+
isExternal: true,
|
|
320
|
+
location: { country: 'GB' },
|
|
321
|
+
referrerHost: 'instagram.com',
|
|
322
|
+
utmCampaign: 'summer-launch',
|
|
323
|
+
group: 'summer-launch',
|
|
324
|
+
placement: 'hero',
|
|
325
|
+
metadata: { pagePath: '/c/demo-collection?pageId=QR123' },
|
|
326
|
+
})
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### `analytics.tag.track(event)`
|
|
330
|
+
|
|
331
|
+
Tracks physical scan analytics such as:
|
|
332
|
+
|
|
333
|
+
- `scan_tag`
|
|
334
|
+
- NFC or QR entry telemetry
|
|
335
|
+
- claim/code activity
|
|
336
|
+
- admin vs customer scan behavior
|
|
337
|
+
|
|
338
|
+
Supported top-level fields include the core scan fields plus the same standard convenience metadata fields, especially `entryType`, `scanMethod`, `group`, `tag`, and campaign or attribution keys when relevant.
|
|
339
|
+
|
|
340
|
+
Like collection events, tag events also accept `visitorId` as a standard top-level field.
|
|
341
|
+
|
|
342
|
+
Example:
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
analytics.tag.track({
|
|
346
|
+
sessionId: 'sess_123',
|
|
347
|
+
eventType: 'scan_tag',
|
|
348
|
+
collectionId: 'demo-collection',
|
|
349
|
+
productId: 'product_1',
|
|
350
|
+
codeId: 'code_123',
|
|
351
|
+
claimId: 'claim_456',
|
|
352
|
+
deviceType: 'mobile',
|
|
353
|
+
path: '/claim/code_123',
|
|
354
|
+
location: {
|
|
355
|
+
country: 'US',
|
|
356
|
+
latitude: 40.7,
|
|
357
|
+
longitude: -74.0,
|
|
358
|
+
area: 35,
|
|
359
|
+
},
|
|
360
|
+
isAdmin: false,
|
|
361
|
+
group: 'retail-batch-a',
|
|
362
|
+
tag: 'promo',
|
|
363
|
+
entryType: 'nfc',
|
|
364
|
+
scanMethod: 'nfc',
|
|
365
|
+
})
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
Notes:
|
|
369
|
+
|
|
370
|
+
- `scan_blank_tag` is intentionally excluded by the backend analytics pipeline
|
|
371
|
+
- Admin scans may still trigger other workflows even when analytics logging is skipped
|
|
372
|
+
|
|
373
|
+
### Queryable metadata
|
|
374
|
+
|
|
375
|
+
Top-level scalar metadata values are the most query-friendly today. You can filter them with `metadata` and break them down with `dimension: 'metadata'` plus `metadataKey`.
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
const grouped = await analytics.admin.breakdown('demo-collection', {
|
|
379
|
+
source: 'tag',
|
|
380
|
+
dimension: 'metadata',
|
|
381
|
+
metadataKey: 'group',
|
|
382
|
+
metric: 'count',
|
|
383
|
+
})
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## Admin Queries
|
|
389
|
+
|
|
390
|
+
All admin analytics routes are collection-scoped:
|
|
391
|
+
|
|
392
|
+
```text
|
|
393
|
+
POST /api/v1/admin/collection/:collectionId/analytics/...
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
### Dashboard-style replacement endpoints
|
|
397
|
+
|
|
398
|
+
These are the easiest starting point if you are building dashboards similar to the older analytics app.
|
|
399
|
+
|
|
400
|
+
#### `analytics.admin.web(collectionId, body)`
|
|
401
|
+
|
|
402
|
+
Returns page-view oriented dashboard data.
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
const dashboard = await analytics.admin.web('demo-collection', {
|
|
406
|
+
startDate: '2026-03-01T00:00:00Z',
|
|
407
|
+
endDate: '2026-03-31T23:59:59Z',
|
|
408
|
+
productId: 'product_1',
|
|
409
|
+
})
|
|
410
|
+
|
|
411
|
+
console.log(dashboard.metrics.totalVisits)
|
|
412
|
+
console.log(dashboard.charts.topPages)
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
#### `analytics.admin.clicks(collectionId, body)`
|
|
416
|
+
|
|
417
|
+
Returns click-focused dashboard data.
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
const clicks = await analytics.admin.clicks('demo-collection', {
|
|
421
|
+
startDate: '2026-03-01T00:00:00Z',
|
|
422
|
+
endDate: '2026-03-31T23:59:59Z',
|
|
423
|
+
})
|
|
424
|
+
|
|
425
|
+
console.log(clicks.metrics.totalClicks)
|
|
426
|
+
console.log(clicks.charts.topExternalLinks)
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
#### `analytics.admin.tagScans(collectionId, body)`
|
|
430
|
+
|
|
431
|
+
Returns tag scan dashboards, risk scoring, and suspicious activity charts.
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
const scans = await analytics.admin.tagScans('demo-collection', {
|
|
435
|
+
productId: 'product_1',
|
|
436
|
+
startDate: '2026-03-01T00:00:00Z',
|
|
437
|
+
endDate: '2026-03-31T23:59:59Z',
|
|
438
|
+
})
|
|
439
|
+
|
|
440
|
+
console.log(scans.metrics.totalScans)
|
|
441
|
+
console.log(scans.charts.suspiciousTags)
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
### Classic web analytics shortcuts
|
|
445
|
+
|
|
446
|
+
If you are building a more traditional analytics UI, the SDK also includes opinionated wrappers for common reports:
|
|
447
|
+
|
|
448
|
+
- `analytics.admin.topPages(...)`
|
|
449
|
+
- `analytics.admin.topReferrers(...)`
|
|
450
|
+
- `analytics.admin.topCampaigns(...)`
|
|
451
|
+
- `analytics.admin.topSources(...)`
|
|
452
|
+
- `analytics.admin.topDestinations(...)`
|
|
453
|
+
|
|
454
|
+
Example:
|
|
455
|
+
|
|
456
|
+
```typescript
|
|
457
|
+
const [pages, referrers, campaigns, destinations] = await Promise.all([
|
|
458
|
+
analytics.admin.topPages('demo-collection', { limit: 10 }),
|
|
459
|
+
analytics.admin.topReferrers('demo-collection', { limit: 10 }),
|
|
460
|
+
analytics.admin.topCampaigns('demo-collection', { limit: 10 }),
|
|
461
|
+
analytics.admin.topDestinations('demo-collection', { limit: 10 }),
|
|
462
|
+
])
|
|
463
|
+
|
|
464
|
+
console.log(pages.rows)
|
|
465
|
+
console.log(referrers.rows)
|
|
466
|
+
console.log(campaigns.rows)
|
|
467
|
+
console.log(destinations.rows)
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
#### `analytics.admin.products(collectionId, body)`
|
|
471
|
+
|
|
472
|
+
Returns distinct product IDs across both analytics domains.
|
|
473
|
+
|
|
474
|
+
```typescript
|
|
475
|
+
const products = await analytics.admin.products('demo-collection')
|
|
476
|
+
console.log(products.products)
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
#### `analytics.admin.qrCodes(collectionId, body)`
|
|
480
|
+
|
|
481
|
+
Returns QR/page identifiers extracted from event URLs.
|
|
482
|
+
|
|
483
|
+
```typescript
|
|
484
|
+
const qrCodes = await analytics.admin.qrCodes('demo-collection')
|
|
485
|
+
console.log(qrCodes)
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
#### `analytics.admin.tags(collectionId, body)`
|
|
489
|
+
|
|
490
|
+
Returns distinct tags with scan counts and active day counts.
|
|
491
|
+
|
|
492
|
+
```typescript
|
|
493
|
+
const tags = await analytics.admin.tags('demo-collection')
|
|
494
|
+
console.log(tags.tags)
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
---
|
|
498
|
+
|
|
499
|
+
## Generic Query Endpoints
|
|
500
|
+
|
|
501
|
+
Use these when you want charts, audits, or your own dashboard logic rather than the replacement dashboard responses.
|
|
502
|
+
|
|
503
|
+
### Summary
|
|
504
|
+
|
|
505
|
+
```typescript
|
|
506
|
+
const summary = await analytics.admin.summary('demo-collection', {
|
|
507
|
+
source: 'events',
|
|
508
|
+
eventType: 'page_view',
|
|
509
|
+
from: '2026-03-01T00:00:00Z',
|
|
510
|
+
to: '2026-03-31T23:59:59Z',
|
|
511
|
+
})
|
|
512
|
+
|
|
513
|
+
console.log(summary.summary.totalEvents)
|
|
514
|
+
console.log(summary.summary.uniqueSessions)
|
|
515
|
+
console.log(summary.summary.uniqueVisitors)
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
### Time series
|
|
519
|
+
|
|
520
|
+
```typescript
|
|
521
|
+
const traffic = await analytics.admin.timeseries('demo-collection', {
|
|
522
|
+
source: 'events',
|
|
523
|
+
eventType: 'page_view',
|
|
524
|
+
granularity: 'week',
|
|
525
|
+
metric: 'uniqueVisitors',
|
|
526
|
+
from: '2026-02-01T00:00:00Z',
|
|
527
|
+
to: '2026-03-01T00:00:00Z',
|
|
528
|
+
})
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
`uniqueVisitors` now works in generic analytics queries. The backend uses `visitorId` when present and falls back to `sessionId` for older events that do not include it yet.
|
|
532
|
+
|
|
533
|
+
### Breakdown
|
|
534
|
+
|
|
535
|
+
```typescript
|
|
536
|
+
const countries = await analytics.admin.breakdown('demo-collection', {
|
|
537
|
+
source: 'events',
|
|
538
|
+
eventType: 'page_view',
|
|
539
|
+
dimension: 'country',
|
|
540
|
+
metric: 'count',
|
|
541
|
+
limit: 25,
|
|
542
|
+
})
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
### Raw events
|
|
546
|
+
|
|
547
|
+
```typescript
|
|
548
|
+
const rows = await analytics.admin.events('demo-collection', {
|
|
549
|
+
source: 'tag',
|
|
550
|
+
eventType: 'scan_tag',
|
|
551
|
+
from: '2026-03-01T00:00:00Z',
|
|
552
|
+
to: '2026-03-31T23:59:59Z',
|
|
553
|
+
limit: 100,
|
|
554
|
+
offset: 0,
|
|
555
|
+
sort: 'desc',
|
|
556
|
+
})
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
---
|
|
560
|
+
|
|
561
|
+
## Legacy-Compatible Convenience Endpoints
|
|
562
|
+
|
|
563
|
+
The SDK exposes wrappers for the preserved compatibility routes too:
|
|
564
|
+
|
|
565
|
+
- `analytics.admin.weekly(...)`
|
|
566
|
+
- `analytics.admin.country(...)`
|
|
567
|
+
|
|
568
|
+
These are useful when migrating existing dashboards that still expect the older analytics app parameter shape.
|
|
569
|
+
|
|
570
|
+
---
|
|
571
|
+
|
|
572
|
+
## Common Filters
|
|
573
|
+
|
|
574
|
+
Most admin analytics queries support combinations of:
|
|
575
|
+
|
|
576
|
+
- `from`, `to`
|
|
577
|
+
- `eventType` or `eventTypes[]`
|
|
578
|
+
- `productId` or `productIds[]`
|
|
579
|
+
- `proofId` or `proofIds[]`
|
|
580
|
+
- `batchId` or `batchIds[]`
|
|
581
|
+
- `variantId` or `variantIds[]`
|
|
582
|
+
- `sessionId` or `sessionIds[]`
|
|
583
|
+
- `country` or `countries[]`
|
|
584
|
+
- `metadata` for top-level JSON equality matching
|
|
585
|
+
|
|
586
|
+
For metrics, generic queries support:
|
|
587
|
+
|
|
588
|
+
- `count`
|
|
589
|
+
- `uniqueSessions`
|
|
590
|
+
- `uniqueVisitors`
|
|
591
|
+
|
|
592
|
+
Extra collection-event filters include:
|
|
593
|
+
|
|
594
|
+
- `appId`, `appIds[]`
|
|
595
|
+
- `destinationAppId`, `destinationAppIds[]`
|
|
596
|
+
- `linkId`, `linkIds[]`
|
|
597
|
+
- `href`, `path`
|
|
598
|
+
- `hrefContains`, `pathContains`
|
|
599
|
+
- `isExternal`
|
|
600
|
+
|
|
601
|
+
Extra tag-event filters include:
|
|
602
|
+
|
|
603
|
+
- `codeId`, `codeIds[]`
|
|
604
|
+
- `claimId`, `claimIds[]`
|
|
605
|
+
- `isAdmin`
|
|
606
|
+
- `hasLocation`
|
|
607
|
+
|
|
608
|
+
---
|
|
609
|
+
|
|
610
|
+
## Best Practices
|
|
611
|
+
|
|
612
|
+
### 1. Use analytics and interactions for different jobs
|
|
613
|
+
|
|
614
|
+
- `analytics` for telemetry, traffic, clicks, and scan monitoring
|
|
615
|
+
- `interactions` for explicit business events and outcomes
|
|
616
|
+
|
|
617
|
+
### 2. Track before navigation
|
|
618
|
+
|
|
619
|
+
For outbound links, log the analytics event immediately before triggering navigation so `sendBeacon()` has the best chance of queuing it.
|
|
620
|
+
|
|
621
|
+
### 3. Keep metadata shallow
|
|
622
|
+
|
|
623
|
+
Analytics metadata filtering currently works best with top-level scalar keys such as:
|
|
624
|
+
|
|
625
|
+
- `placement`
|
|
626
|
+
- `campaign`
|
|
627
|
+
- `group`
|
|
628
|
+
- `tag`
|
|
629
|
+
- `referrerHost`
|
|
630
|
+
- `utmSource`
|
|
631
|
+
- `utmCampaign`
|
|
632
|
+
- `pagePath`
|
|
633
|
+
- `scanMethod`
|
|
634
|
+
|
|
635
|
+
See [docs/analytics-metadata-conventions.md](analytics-metadata-conventions.md) for the recommended shared vocabulary.
|
|
636
|
+
|
|
637
|
+
### 4. Prefer generic endpoints for custom dashboards
|
|
638
|
+
|
|
639
|
+
Use `/summary`, `/timeseries`, `/breakdown`, and `/events` when you are building your own reporting UI. Use `web`, `clicks`, and `tagScans` when you want opinionated dashboard responses.
|
|
640
|
+
|
|
641
|
+
---
|
|
642
|
+
|
|
643
|
+
## Example: Outbound Link Tracking
|
|
644
|
+
|
|
645
|
+
```typescript
|
|
646
|
+
function trackAndNavigate(href: string) {
|
|
647
|
+
analytics.collection.track({
|
|
648
|
+
sessionId: 'sess_123',
|
|
649
|
+
eventType: 'click_link',
|
|
650
|
+
collectionId: 'demo-collection',
|
|
651
|
+
linkId: 'buy-now',
|
|
652
|
+
href,
|
|
653
|
+
isExternal: true,
|
|
654
|
+
placement: 'hero',
|
|
655
|
+
pagePath: '/c/demo-collection',
|
|
656
|
+
})
|
|
657
|
+
|
|
658
|
+
window.location.href = href
|
|
659
|
+
}
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
## Example: Scan Dashboard
|
|
663
|
+
|
|
664
|
+
```typescript
|
|
665
|
+
async function loadScanDashboard(collectionId: string) {
|
|
666
|
+
const [summary, countries, suspicious] = await Promise.all([
|
|
667
|
+
analytics.admin.summary(collectionId, {
|
|
668
|
+
source: 'tag',
|
|
669
|
+
eventType: 'scan_tag',
|
|
670
|
+
}),
|
|
671
|
+
analytics.admin.breakdown(collectionId, {
|
|
672
|
+
source: 'tag',
|
|
673
|
+
eventType: 'scan_tag',
|
|
674
|
+
dimension: 'country',
|
|
675
|
+
metric: 'count',
|
|
676
|
+
limit: 10,
|
|
677
|
+
}),
|
|
678
|
+
analytics.admin.tagScans(collectionId, {
|
|
679
|
+
startDate: '2026-03-01T00:00:00Z',
|
|
680
|
+
endDate: '2026-03-31T23:59:59Z',
|
|
681
|
+
}),
|
|
682
|
+
])
|
|
683
|
+
|
|
684
|
+
return {
|
|
685
|
+
totalScans: summary.summary.totalEvents,
|
|
686
|
+
topCountries: countries.rows,
|
|
687
|
+
suspiciousTags: suspicious.charts.suspiciousTags,
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
```
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
The `interactions` namespace is a **critical pattern** for tracking user engagement. Many apps rely heavily on it for logging events that can trigger journeys, feed analytics dashboards, and drive aggregated results such as vote counts.
|
|
4
4
|
|
|
5
|
+
If you want generic page analytics, outbound click tracking, QR landing telemetry, or tag scan dashboards, use [analytics.md](analytics.md) instead. `interactions` is for structured business events and outcomes, not general web analytics.
|
|
6
|
+
|
|
5
7
|
---
|
|
6
8
|
|
|
7
9
|
## Overview
|
package/dist/docs/overview.md
CHANGED
|
@@ -47,6 +47,7 @@ The SmartLinks SDK (`@proveanything/smartlinks`) includes comprehensive document
|
|
|
47
47
|
| **API Reference** | `docs/API_SUMMARY.md` | Complete SDK function reference, types, error handling |
|
|
48
48
|
| **Multi-Page Architecture** | `docs/mpa.md` | Build pipeline, entry points, multi-page setup, content hashing |
|
|
49
49
|
| **AI & Chat** | `docs/ai.md` | Chat completions, RAG, streaming, tool calling, voice, podcasts, TTS |
|
|
50
|
+
| **Analytics** | `docs/analytics.md` | Fire-and-forget page/click/tag analytics plus admin dashboard queries |
|
|
50
51
|
| **Theming** | `docs/theme.system.md` | Implementing dynamic themes via URL params or postMessage |
|
|
51
52
|
| **Theme Defaults** | `docs/theme-defaults.md` | Default colour values for light/dark modes |
|
|
52
53
|
| **Internationalization** | `docs/i18n.md` | Adding multi-language support, translation patterns |
|
|
@@ -54,7 +55,7 @@ The SmartLinks SDK (`@proveanything/smartlinks`) includes comprehensive document
|
|
|
54
55
|
| **Containers** | `docs/containers.md` | Building full-app embeddable containers (lazy-loaded) |
|
|
55
56
|
| **Executors** | `docs/executor.md` | Building executor bundles for SEO, LLM content, programmatic config |
|
|
56
57
|
| **Deep Linking** | `docs/deep-link-discovery.md` | URL state management, navigable states, portal menus, AI nav |
|
|
57
|
-
| **Interactions** | `docs/interactions.md` |
|
|
58
|
+
| **Interactions** | `docs/interactions.md` | Business events, outcomes, voting, competitions, and journey triggers |
|
|
58
59
|
| **AI-Native Manifests** | `docs/manifests.md` | `app.manifest.json`, `app.admin.json`, `ai-guide.md` structure |
|
|
59
60
|
| **App Config Files** | `docs/app-manifest.md` | Full field-by-field reference for both JSON config files |
|
|
60
61
|
| **Real-time Messaging** | `docs/realtime.md` | Adding Ably real-time features (chat, live updates) |
|