@proveanything/smartlinks 1.8.0 → 1.8.1

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.
@@ -1,5 +1,5 @@
1
- import type { AnalyticsTrackOptions, AnalyticsTrackResult, AnalyticsBrowserConfig, AnalyticsGeolocationCaptureOptions, AnalyticsLinkClickInput, AnalyticsLinkBindingOptions, AnalyticsPageViewBindingOptions, AnalyticsClassicReportRequest, AnalyticsVisitorIdOptions, CollectionAnalyticsEvent, TagAnalyticsEvent, AnalyticsSummaryRequest, AnalyticsSummaryResponse, AnalyticsTimeseriesRequest, AnalyticsTimeseriesResponse, AnalyticsBreakdownRequest, AnalyticsBreakdownResponse, AnalyticsEventsRequest, AnalyticsEventsResponse, LegacyAnalyticsRequest, AnalyticsDashboardResponse, AnalyticsProductsResponse, AnalyticsQrCodesResponse, AnalyticsTagsResponse, AnalyticsWeeklyRequest, AnalyticsCountryRequest } from "../types/analytics";
2
- export type { AnalyticsTrackOptions, AnalyticsTrackResult, AnalyticsBrowserConfig, AnalyticsGeolocationCaptureOptions, AnalyticsLinkClickInput, AnalyticsLinkBindingOptions, AnalyticsPageViewBindingOptions, AnalyticsClassicReportRequest, AnalyticsVisitorIdOptions, CollectionAnalyticsEvent, TagAnalyticsEvent, AnalyticsSummaryRequest, AnalyticsSummaryResponse, AnalyticsTimeseriesRequest, AnalyticsTimeseriesResponse, AnalyticsBreakdownRequest, AnalyticsBreakdownResponse, AnalyticsEventsRequest, AnalyticsEventsResponse, LegacyAnalyticsRequest, AnalyticsDashboardResponse, AnalyticsProductsResponse, AnalyticsQrCodesResponse, AnalyticsTagsResponse, AnalyticsWeeklyRequest, AnalyticsCountryRequest, };
1
+ import type { AnalyticsTrackOptions, AnalyticsTrackResult, AnalyticsBrowserConfig, AnalyticsGeolocationCaptureOptions, AnalyticsLinkClickInput, AnalyticsLinkBindingOptions, AnalyticsPageViewBindingOptions, AnalyticsClassicReportRequest, AnalyticsVisitorIdOptions, AnalyticsSessionId, CollectionAnalyticsEvent, TagAnalyticsEvent, AnalyticsSummaryRequest, AnalyticsSummaryResponse, AnalyticsTimeseriesRequest, AnalyticsTimeseriesResponse, AnalyticsBreakdownRequest, AnalyticsBreakdownResponse, AnalyticsEventsRequest, AnalyticsEventsResponse, LegacyAnalyticsRequest, AnalyticsDashboardResponse, AnalyticsProductsResponse, AnalyticsQrCodesResponse, AnalyticsTagsResponse, AnalyticsWeeklyRequest, AnalyticsCountryRequest } from "../types/analytics";
2
+ export type { AnalyticsTrackOptions, AnalyticsTrackResult, AnalyticsBrowserConfig, AnalyticsGeolocationCaptureOptions, AnalyticsLinkClickInput, AnalyticsLinkBindingOptions, AnalyticsPageViewBindingOptions, AnalyticsClassicReportRequest, AnalyticsVisitorIdOptions, AnalyticsSessionId, CollectionAnalyticsEvent, TagAnalyticsEvent, AnalyticsSummaryRequest, AnalyticsSummaryResponse, AnalyticsTimeseriesRequest, AnalyticsTimeseriesResponse, AnalyticsBreakdownRequest, AnalyticsBreakdownResponse, AnalyticsEventsRequest, AnalyticsEventsResponse, LegacyAnalyticsRequest, AnalyticsDashboardResponse, AnalyticsProductsResponse, AnalyticsQrCodesResponse, AnalyticsTagsResponse, AnalyticsWeeklyRequest, AnalyticsCountryRequest, };
3
3
  export declare namespace analytics {
4
4
  namespace collection {
5
5
  /**
@@ -17,7 +17,7 @@ export declare namespace analytics {
17
17
  }
18
18
  namespace browser {
19
19
  function configure(config: AnalyticsBrowserConfig): void;
20
- function getSessionId(): string | undefined;
20
+ function getSessionId(): AnalyticsSessionId | undefined;
21
21
  function getVisitorId(): string | undefined;
22
22
  function setVisitorId(visitorId: string, options?: AnalyticsVisitorIdOptions): string;
23
23
  function clearVisitorId(options?: Pick<AnalyticsVisitorIdOptions, 'storage' | 'storageKey'>): void;
@@ -39,8 +39,15 @@ const defaultCampaignParamMap = {
39
39
  qrCodeId: 'qrCodeId',
40
40
  scanMethod: ['scanMethod', 'scan_method'],
41
41
  };
42
+ const promotedAnalyticsKeys = new Set([
43
+ 'visitorId',
44
+ 'referrerHost',
45
+ 'pageId',
46
+ 'entryType',
47
+ 'scanMethod',
48
+ ]);
42
49
  function createSessionId() {
43
- return `${Date.now()}_${Math.floor(Math.random() * 1000000)}`;
50
+ return Date.now() * 1000 + Math.floor(Math.random() * 1000);
44
51
  }
45
52
  function createVisitorId() {
46
53
  return `visitor_${Date.now()}_${Math.floor(Math.random() * 1000000)}`;
@@ -56,6 +63,33 @@ function getStorage(mode) {
56
63
  }
57
64
  return undefined;
58
65
  }
66
+ function parseStoredSessionId(value) {
67
+ if (/^\d+$/.test(value)) {
68
+ const parsed = Number(value);
69
+ if (Number.isSafeInteger(parsed))
70
+ return parsed;
71
+ }
72
+ return undefined;
73
+ }
74
+ function assertValidSessionId(sessionId) {
75
+ if (sessionId === undefined || sessionId === null)
76
+ return undefined;
77
+ if (typeof sessionId !== 'number' || !Number.isSafeInteger(sessionId)) {
78
+ throw new Error('analytics sessionId must be a safe integer number.');
79
+ }
80
+ return sessionId;
81
+ }
82
+ function prunePromotedMetadataKeys(metadata) {
83
+ if (!metadata)
84
+ return undefined;
85
+ const entries = Object.entries(metadata).filter(([key]) => !promotedAnalyticsKeys.has(key));
86
+ if (entries.length === 0)
87
+ return undefined;
88
+ return Object.fromEntries(entries);
89
+ }
90
+ function normalizeAnalyticsEvent(event) {
91
+ return Object.assign(Object.assign({}, event), { sessionId: assertValidSessionId(event.sessionId), metadata: prunePromotedMetadataKeys(event.metadata) });
92
+ }
59
93
  function getOrCreateSessionId() {
60
94
  var _a, _b, _c, _d;
61
95
  if (analyticsBrowserState.sessionId)
@@ -64,14 +98,17 @@ function getOrCreateSessionId() {
64
98
  const storage = getStorage('session');
65
99
  const existing = key ? storage === null || storage === void 0 ? void 0 : storage.getItem(key) : undefined;
66
100
  if (existing) {
67
- analyticsBrowserState.sessionId = existing;
68
- return existing;
101
+ const parsed = parseStoredSessionId(existing);
102
+ if (parsed !== undefined) {
103
+ analyticsBrowserState.sessionId = parsed;
104
+ return parsed;
105
+ }
69
106
  }
70
- const generated = (_d = (_c = (_b = analyticsBrowserState.config).sessionIdFactory) === null || _c === void 0 ? void 0 : _c.call(_b)) !== null && _d !== void 0 ? _d : createSessionId();
107
+ const generated = assertValidSessionId((_d = (_c = (_b = analyticsBrowserState.config).sessionIdFactory) === null || _c === void 0 ? void 0 : _c.call(_b)) !== null && _d !== void 0 ? _d : createSessionId());
71
108
  analyticsBrowserState.sessionId = generated;
72
109
  if (key) {
73
110
  try {
74
- storage === null || storage === void 0 ? void 0 : storage.setItem(key, generated);
111
+ storage === null || storage === void 0 ? void 0 : storage.setItem(key, String(generated));
75
112
  }
76
113
  catch (_e) {
77
114
  }
@@ -201,21 +238,21 @@ function getResolvedLocation() {
201
238
  return undefined;
202
239
  }
203
240
  function mergeCollectionEventDefaults(event) {
204
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
241
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
205
242
  const configuredDefaults = (_a = analyticsBrowserState.config.defaultCollectionEvent) !== null && _a !== void 0 ? _a : {};
206
243
  const dynamicDefaults = (_d = (_c = (_b = analyticsBrowserState.config).getCollectionDefaults) === null || _c === void 0 ? void 0 : _c.call(_b)) !== null && _d !== void 0 ? _d : {};
207
244
  const campaignFields = analyticsBrowserState.config.autoCaptureCampaignParams === false ? {} : getCampaignFields();
208
245
  const path = (_e = event.path) !== null && _e !== void 0 ? _e : getCurrentPath();
209
246
  const visitorId = (_f = event.visitorId) !== null && _f !== void 0 ? _f : getOrCreateVisitorId();
210
247
  const href = (_h = (_g = event.href) !== null && _g !== void 0 ? _g : dynamicDefaults.href) !== null && _h !== void 0 ? _h : configuredDefaults.href;
211
- return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, configuredDefaults), dynamicDefaults), campaignFields), getCurrentReferrerFields()), { visitorId, sessionId: getOrCreateSessionId(), deviceType: detectDeviceType(), path, pagePath: (_k = (_j = event.pagePath) !== null && _j !== void 0 ? _j : campaignFields.pagePath) !== null && _k !== void 0 ? _k : path, destinationDomain: (_l = event.destinationDomain) !== null && _l !== void 0 ? _l : getDestinationDomain(href), location: getResolvedLocation(), eventType: 'page_view' }), event), { metadata: Object.assign(Object.assign(Object.assign(Object.assign({}, ((_m = configuredDefaults.metadata) !== null && _m !== void 0 ? _m : {})), ((_o = dynamicDefaults.metadata) !== null && _o !== void 0 ? _o : {})), (visitorId ? { visitorId } : {})), ((_p = event.metadata) !== null && _p !== void 0 ? _p : {})) });
248
+ return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, configuredDefaults), dynamicDefaults), campaignFields), getCurrentReferrerFields()), { visitorId, sessionId: assertValidSessionId((_j = event.sessionId) !== null && _j !== void 0 ? _j : getOrCreateSessionId()), deviceType: detectDeviceType(), path, pagePath: (_l = (_k = event.pagePath) !== null && _k !== void 0 ? _k : campaignFields.pagePath) !== null && _l !== void 0 ? _l : path, destinationDomain: (_m = event.destinationDomain) !== null && _m !== void 0 ? _m : getDestinationDomain(href), location: getResolvedLocation(), eventType: 'page_view' }), event), { metadata: prunePromotedMetadataKeys(Object.assign(Object.assign(Object.assign({}, ((_o = configuredDefaults.metadata) !== null && _o !== void 0 ? _o : {})), ((_p = dynamicDefaults.metadata) !== null && _p !== void 0 ? _p : {})), ((_q = event.metadata) !== null && _q !== void 0 ? _q : {}))) });
212
249
  }
213
250
  function mergeTagEventDefaults(event) {
214
- var _a, _b, _c, _d, _e, _f, _g, _h;
251
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
215
252
  const configuredDefaults = (_a = analyticsBrowserState.config.defaultTagEvent) !== null && _a !== void 0 ? _a : {};
216
253
  const dynamicDefaults = (_d = (_c = (_b = analyticsBrowserState.config).getTagDefaults) === null || _c === void 0 ? void 0 : _c.call(_b)) !== null && _d !== void 0 ? _d : {};
217
254
  const visitorId = (_e = event.visitorId) !== null && _e !== void 0 ? _e : getOrCreateVisitorId();
218
- return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, configuredDefaults), dynamicDefaults), { visitorId, sessionId: getOrCreateSessionId(), deviceType: detectDeviceType(), location: getResolvedLocation(), eventType: 'scan_tag' }), event), { metadata: Object.assign(Object.assign(Object.assign(Object.assign({}, ((_f = configuredDefaults.metadata) !== null && _f !== void 0 ? _f : {})), ((_g = dynamicDefaults.metadata) !== null && _g !== void 0 ? _g : {})), (visitorId ? { visitorId } : {})), ((_h = event.metadata) !== null && _h !== void 0 ? _h : {})) });
255
+ return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, configuredDefaults), dynamicDefaults), { visitorId, sessionId: assertValidSessionId((_f = event.sessionId) !== null && _f !== void 0 ? _f : getOrCreateSessionId()), deviceType: detectDeviceType(), location: getResolvedLocation(), eventType: 'scan_tag' }), event), { metadata: prunePromotedMetadataKeys(Object.assign(Object.assign(Object.assign({}, ((_g = configuredDefaults.metadata) !== null && _g !== void 0 ? _g : {})), ((_h = dynamicDefaults.metadata) !== null && _h !== void 0 ? _h : {})), ((_j = event.metadata) !== null && _j !== void 0 ? _j : {}))) });
219
256
  }
220
257
  function isExternalHref(href) {
221
258
  if (typeof window === 'undefined')
@@ -267,7 +304,7 @@ export var analytics;
267
304
  * Uses `navigator.sendBeacon()` when available, falling back to `fetch(..., { keepalive: true })`.
268
305
  */
269
306
  function track(event, options) {
270
- return queueAnalytics('/public/analytics/collection', event, options);
307
+ return queueAnalytics('/public/analytics/collection', normalizeAnalyticsEvent(event), options);
271
308
  }
272
309
  collection.track = track;
273
310
  })(collection = analytics.collection || (analytics.collection = {}));
@@ -278,7 +315,7 @@ export var analytics;
278
315
  * Uses `navigator.sendBeacon()` when available, falling back to `fetch(..., { keepalive: true })`.
279
316
  */
280
317
  function track(event, options) {
281
- return queueAnalytics('/public/analytics/tag', event, options);
318
+ return queueAnalytics('/public/analytics/tag', normalizeAnalyticsEvent(event), options);
282
319
  }
283
320
  tag.track = track;
284
321
  })(tag = analytics.tag || (analytics.tag = {}));
@@ -1,6 +1,6 @@
1
1
  # Smartlinks API Summary
2
2
 
3
- Version: 1.8.0 | Generated: 2026-03-12T19:37:47.506Z
3
+ Version: 1.8.1 | Generated: 2026-03-14T09:21:59.178Z
4
4
 
5
5
  This is a concise summary of all available API functions and types.
6
6
 
@@ -938,9 +938,9 @@ interface AnalyticsLocation {
938
938
  }
939
939
  ```
940
940
 
941
- **AnalyticsStandardMetadataFields** (interface)
941
+ **AnalyticsStandardEventFields** (interface)
942
942
  ```typescript
943
- interface AnalyticsStandardMetadataFields {
943
+ interface AnalyticsStandardEventFields {
944
944
  visitorId?: string
945
945
  referrer?: string
946
946
  referrerHost?: string
@@ -977,13 +977,13 @@ interface AnalyticsTrackOptions {
977
977
  ```typescript
978
978
  interface AnalyticsBrowserConfig {
979
979
  sessionStorageKey?: string
980
- sessionIdFactory?: () => string
980
+ sessionIdFactory?: () => AnalyticsSessionId
981
981
  visitorId?: string
982
982
  visitorStorage?: AnalyticsStorageMode
983
983
  visitorStorageKey?: string
984
984
  visitorIdFactory?: () => string
985
985
  autoCaptureCampaignParams?: boolean
986
- campaignParamMap?: Partial<Record<keyof AnalyticsStandardMetadataFields, string | string[]>>
986
+ campaignParamMap?: Partial<Record<keyof AnalyticsStandardEventFields, string | string[]>>
987
987
  defaultCollectionEvent?: Partial<CollectionAnalyticsEvent>
988
988
  defaultTagEvent?: Partial<TagAnalyticsEvent>
989
989
  getCollectionDefaults?: () => Partial<CollectionAnalyticsEvent> | undefined
@@ -1048,8 +1048,8 @@ interface AnalyticsFilterRequest {
1048
1048
  batchIds?: string[]
1049
1049
  variantId?: string
1050
1050
  variantIds?: string[]
1051
- sessionId?: string
1052
- sessionIds?: string[]
1051
+ sessionId?: AnalyticsSessionId
1052
+ sessionIds?: AnalyticsSessionId[]
1053
1053
  country?: string
1054
1054
  countries?: string[]
1055
1055
  metadata?: Record<string, any>
@@ -1256,6 +1256,8 @@ interface AnalyticsTagsResponse {
1256
1256
 
1257
1257
  **AnalyticsStorageMode** = `'local' | 'session' | false`
1258
1258
 
1259
+ **AnalyticsSessionId** = `number`
1260
+
1259
1261
  **EventAnalyticsDimension** = ``
1260
1262
 
1261
1263
  **TagAnalyticsDimension** = ``
@@ -5789,7 +5791,7 @@ Fire-and-forget tag analytics event. Uses `navigator.sendBeacon()` when availabl
5789
5791
  **configure**(config: AnalyticsBrowserConfig) → `void`
5790
5792
  Fire-and-forget tag analytics event. Uses `navigator.sendBeacon()` when available, falling back to `fetch(..., { keepalive: true })`.
5791
5793
 
5792
- **getSessionId**() → `string | undefined`
5794
+ **getSessionId**() → `AnalyticsSessionId | undefined`
5793
5795
  Fire-and-forget tag analytics event. Uses `navigator.sendBeacon()` when available, falling back to `fetch(..., { keepalive: true })`.
5794
5796
 
5795
5797
  **getVisitorId**() → `string | undefined`
@@ -1,26 +1,31 @@
1
1
  # Analytics Metadata Conventions
2
2
 
3
- Use these as the recommended standard keys for analytics event metadata.
3
+ Use these as the recommended standard analytics keys.
4
4
 
5
- You can put them in `metadata` directly today. The public analytics ingestion endpoints also accept these as top-level convenience fields and mirror them into `metadata` on the backend.
5
+ Some of these are now promoted top-level analytics fields. Others remain good metadata keys for custom dimensions.
6
6
 
7
7
  ---
8
8
 
9
9
  ## Recommended Keys
10
10
 
11
- ### Traffic and attribution
11
+ ### Promoted top-level fields
12
12
 
13
- - `referrer`
13
+ - `visitorId`
14
14
  - `referrerHost`
15
+ - `entryType`
16
+ - `pageId`
17
+ - `scanMethod`
18
+
19
+ These should be sent as top-level analytics fields, not inside `metadata`.
20
+
21
+ ### Metadata-friendly keys
22
+
23
+ - `referrer`
15
24
  - `utmSource`
16
25
  - `utmMedium`
17
26
  - `utmCampaign`
18
27
  - `utmContent`
19
28
  - `utmTerm`
20
- - `entryType` — for example `direct`, `qr`, `nfc`, `social`, `email`, `paid`, or `organic`
21
-
22
- ### Link-tree and page analytics
23
-
24
29
  - `group`
25
30
  - `tag`
26
31
  - `campaign`
@@ -31,13 +36,8 @@ You can put them in `metadata` directly today. The public analytics ingestion en
31
36
  - `linkTitle`
32
37
  - `destinationDomain`
33
38
  - `pagePath`
34
- - `pageId`
35
39
  - `qrCodeId`
36
40
 
37
- ### Physical scan analytics
38
-
39
- - `scanMethod` — for example `nfc` or `qr`
40
-
41
41
  ---
42
42
 
43
43
  ## Why These Matter
@@ -56,6 +56,7 @@ These keys give teams a shared vocabulary for:
56
56
 
57
57
  - Treat these as reserved standard keys.
58
58
  - Prefer these names before inventing custom alternatives.
59
+ - Send promoted fields at top level.
59
60
  - Keep values flat and scalar where possible so they are easier to filter and break down later.
60
61
  - Promote a field to a first-class backend column only when it becomes a hot platform-wide dimension.
61
62
 
@@ -65,14 +66,17 @@ These keys give teams a shared vocabulary for:
65
66
 
66
67
  ```typescript
67
68
  analytics.collection.track({
68
- sessionId: 'sess_123',
69
+ sessionId: 1234567890,
69
70
  eventType: 'click_link',
70
71
  collectionId: 'demo-collection',
72
+ visitorId: 'visitor_123',
71
73
  linkId: 'hero-cta',
72
74
  href: 'https://example.com/buy',
75
+ referrerHost: 'instagram.com',
73
76
  placement: 'hero',
74
77
  campaign: 'summer-launch',
75
78
  utmSource: 'email',
79
+ pageId: 'QR123',
76
80
  metadata: {
77
81
  pagePath: '/c/demo-collection',
78
82
  },
@@ -44,7 +44,7 @@ There are two analytics domains:
44
44
  | Collection events | `/public/analytics/collection` | Page views, clicks, app navigation, landing pages |
45
45
  | Tag events | `/public/analytics/tag` | NFC / QR scans, claim/code activity, suspicious scan monitoring |
46
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.
47
+ The backend stores custom analytics dimensions in `metadata`, but promoted analytics fields now belong at top level and are queried from real columns.
48
48
 
49
49
  See [docs/analytics-metadata-conventions.md](analytics-metadata-conventions.md) for the recommended key set.
50
50
 
@@ -60,7 +60,7 @@ import { initializeApi, analytics } from '@proveanything/smartlinks'
60
60
  initializeApi({ baseURL: 'https://smartlinks.app/api/v1' })
61
61
 
62
62
  analytics.collection.track({
63
- sessionId: 'sess_123',
63
+ sessionId: 1234567890,
64
64
  eventType: 'page_view',
65
65
  collectionId: 'demo-collection',
66
66
  productId: 'product_1',
@@ -74,7 +74,7 @@ analytics.collection.track({
74
74
 
75
75
  ```typescript
76
76
  analytics.collection.track({
77
- sessionId: 'sess_123',
77
+ sessionId: 1234567890,
78
78
  eventType: 'click_link',
79
79
  collectionId: 'demo-collection',
80
80
  productId: 'product_1',
@@ -91,7 +91,7 @@ analytics.collection.track({
91
91
 
92
92
  ```typescript
93
93
  analytics.tag.track({
94
- sessionId: 'sess_123',
94
+ sessionId: 1234567890,
95
95
  eventType: 'scan_tag',
96
96
  collectionId: 'demo-collection',
97
97
  productId: 'product_1',
@@ -298,15 +298,13 @@ Tracks generic collection analytics events such as:
298
298
  - internal navigation
299
299
  - outbound link activity
300
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.
301
+ Supported top-level fields include the core event fields plus promoted analytics columns such as `visitorId`, `referrerHost`, `pageId`, and `entryType`, along with custom metadata dimensions like `group`, `placement`, `pagePath`, and `qrCodeId`.
304
302
 
305
303
  Example:
306
304
 
307
305
  ```typescript
308
306
  analytics.collection.track({
309
- sessionId: 'sess_123',
307
+ sessionId: 1234567890,
310
308
  eventType: 'page_view',
311
309
  collectionId: 'demo-collection',
312
310
  productId: 'product_1',
@@ -322,6 +320,7 @@ analytics.collection.track({
322
320
  utmCampaign: 'summer-launch',
323
321
  group: 'summer-launch',
324
322
  placement: 'hero',
323
+ pageId: 'QR123',
325
324
  metadata: { pagePath: '/c/demo-collection?pageId=QR123' },
326
325
  })
327
326
  ```
@@ -335,15 +334,13 @@ Tracks physical scan analytics such as:
335
334
  - claim/code activity
336
335
  - admin vs customer scan behavior
337
336
 
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.
337
+ Supported top-level fields include the core scan fields plus promoted analytics columns such as `visitorId`, `entryType`, and `scanMethod`, along with custom metadata dimensions like `group`, `tag`, and campaign extras.
341
338
 
342
339
  Example:
343
340
 
344
341
  ```typescript
345
342
  analytics.tag.track({
346
- sessionId: 'sess_123',
343
+ sessionId: 1234567890,
347
344
  eventType: 'scan_tag',
348
345
  collectionId: 'demo-collection',
349
346
  productId: 'product_1',
@@ -374,6 +371,8 @@ Notes:
374
371
 
375
372
  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
373
 
374
+ Promoted fields such as `visitorId`, `referrerHost`, `pageId`, `entryType`, and `scanMethod` should be sent and queried as top-level fields, not inside `metadata`.
375
+
377
376
  ```typescript
378
377
  const grouped = await analytics.admin.breakdown('demo-collection', {
379
378
  source: 'tag',
@@ -528,7 +527,7 @@ const traffic = await analytics.admin.timeseries('demo-collection', {
528
527
  })
529
528
  ```
530
529
 
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.
530
+ `uniqueVisitors` now works in generic analytics queries. The backend uses the top-level `visitorId` column when present and falls back to numeric `sessionId` for older events that do not include it yet.
532
531
 
533
532
  ### Breakdown
534
533
 
@@ -579,7 +578,7 @@ Most admin analytics queries support combinations of:
579
578
  - `proofId` or `proofIds[]`
580
579
  - `batchId` or `batchIds[]`
581
580
  - `variantId` or `variantIds[]`
582
- - `sessionId` or `sessionIds[]`
581
+ - `sessionId` or `sessionIds[]` as numbers
583
582
  - `country` or `countries[]`
584
583
  - `metadata` for top-level JSON equality matching
585
584
 
@@ -626,11 +625,9 @@ Analytics metadata filtering currently works best with top-level scalar keys suc
626
625
  - `campaign`
627
626
  - `group`
628
627
  - `tag`
629
- - `referrerHost`
630
628
  - `utmSource`
631
629
  - `utmCampaign`
632
630
  - `pagePath`
633
- - `scanMethod`
634
631
 
635
632
  See [docs/analytics-metadata-conventions.md](analytics-metadata-conventions.md) for the recommended shared vocabulary.
636
633
 
@@ -645,7 +642,7 @@ Use `/summary`, `/timeseries`, `/breakdown`, and `/events` when you are building
645
642
  ```typescript
646
643
  function trackAndNavigate(href: string) {
647
644
  analytics.collection.track({
648
- sessionId: 'sess_123',
645
+ sessionId: 1234567890,
649
646
  eventType: 'click_link',
650
647
  collectionId: 'demo-collection',
651
648
  linkId: 'buy-now',
package/dist/openapi.yaml CHANGED
@@ -10782,7 +10782,7 @@ components:
10782
10782
  type: number
10783
10783
  area:
10784
10784
  type: number
10785
- AnalyticsStandardMetadataFields:
10785
+ AnalyticsStandardEventFields:
10786
10786
  type: object
10787
10787
  properties:
10788
10788
  visitorId:
@@ -10834,7 +10834,7 @@ components:
10834
10834
  type: object
10835
10835
  properties:
10836
10836
  sessionId:
10837
- type: string
10837
+ $ref: "#/components/schemas/AnalyticsSessionId"
10838
10838
  eventType:
10839
10839
  $ref: "#/components/schemas/AnalyticsEventType"
10840
10840
  collectionId:
@@ -10873,7 +10873,7 @@ components:
10873
10873
  type: object
10874
10874
  properties:
10875
10875
  sessionId:
10876
- type: string
10876
+ $ref: "#/components/schemas/AnalyticsSessionId"
10877
10877
  eventType:
10878
10878
  $ref: "#/components/schemas/AnalyticsEventType"
10879
10879
  collectionId:
@@ -11048,11 +11048,11 @@ components:
11048
11048
  items:
11049
11049
  type: string
11050
11050
  sessionId:
11051
- type: string
11051
+ $ref: "#/components/schemas/AnalyticsSessionId"
11052
11052
  sessionIds:
11053
11053
  type: array
11054
11054
  items:
11055
- type: string
11055
+ $ref: "#/components/schemas/AnalyticsSessionId"
11056
11056
  country:
11057
11057
  type: string
11058
11058
  countries:
@@ -11,6 +11,7 @@ export type AnalyticsMetric = 'count' | 'uniqueSessions' | 'uniqueVisitors';
11
11
  export type AnalyticsSortOrder = 'asc' | 'desc';
12
12
  export type AnalyticsDeviceType = 'mobile' | 'tablet' | 'desktop' | 'unknown';
13
13
  export type AnalyticsStorageMode = 'local' | 'session' | false;
14
+ export type AnalyticsSessionId = number;
14
15
  export interface AnalyticsLocation {
15
16
  country?: string;
16
17
  latitude?: number;
@@ -18,7 +19,7 @@ export interface AnalyticsLocation {
18
19
  area?: number;
19
20
  [key: string]: any;
20
21
  }
21
- export interface AnalyticsStandardMetadataFields {
22
+ export interface AnalyticsStandardEventFields {
22
23
  visitorId?: string;
23
24
  referrer?: string;
24
25
  referrerHost?: string;
@@ -42,8 +43,8 @@ export interface AnalyticsStandardMetadataFields {
42
43
  qrCodeId?: string;
43
44
  scanMethod?: string;
44
45
  }
45
- export interface CollectionAnalyticsEvent extends AnalyticsStandardMetadataFields {
46
- sessionId?: string;
46
+ export interface CollectionAnalyticsEvent extends AnalyticsStandardEventFields {
47
+ sessionId?: AnalyticsSessionId;
47
48
  eventType: AnalyticsEventType;
48
49
  collectionId: string;
49
50
  productId?: string;
@@ -60,8 +61,8 @@ export interface CollectionAnalyticsEvent extends AnalyticsStandardMetadataField
60
61
  location?: AnalyticsLocation;
61
62
  metadata?: Record<string, any>;
62
63
  }
63
- export interface TagAnalyticsEvent extends AnalyticsStandardMetadataFields {
64
- sessionId?: string;
64
+ export interface TagAnalyticsEvent extends AnalyticsStandardEventFields {
65
+ sessionId?: AnalyticsSessionId;
65
66
  eventType: AnalyticsEventType;
66
67
  collectionId: string;
67
68
  productId?: string;
@@ -81,13 +82,13 @@ export interface AnalyticsTrackOptions {
81
82
  }
82
83
  export interface AnalyticsBrowserConfig {
83
84
  sessionStorageKey?: string;
84
- sessionIdFactory?: () => string;
85
+ sessionIdFactory?: () => AnalyticsSessionId;
85
86
  visitorId?: string;
86
87
  visitorStorage?: AnalyticsStorageMode;
87
88
  visitorStorageKey?: string;
88
89
  visitorIdFactory?: () => string;
89
90
  autoCaptureCampaignParams?: boolean;
90
- campaignParamMap?: Partial<Record<keyof AnalyticsStandardMetadataFields, string | string[]>>;
91
+ campaignParamMap?: Partial<Record<keyof AnalyticsStandardEventFields, string | string[]>>;
91
92
  defaultCollectionEvent?: Partial<CollectionAnalyticsEvent>;
92
93
  defaultTagEvent?: Partial<TagAnalyticsEvent>;
93
94
  getCollectionDefaults?: () => Partial<CollectionAnalyticsEvent> | undefined;
@@ -148,8 +149,8 @@ export interface AnalyticsFilterRequest {
148
149
  batchIds?: string[];
149
150
  variantId?: string;
150
151
  variantIds?: string[];
151
- sessionId?: string;
152
- sessionIds?: string[];
152
+ sessionId?: AnalyticsSessionId;
153
+ sessionIds?: AnalyticsSessionId[];
153
154
  country?: string;
154
155
  countries?: string[];
155
156
  metadata?: Record<string, any>;
@@ -1,6 +1,6 @@
1
1
  # Smartlinks API Summary
2
2
 
3
- Version: 1.8.0 | Generated: 2026-03-12T19:37:47.506Z
3
+ Version: 1.8.1 | Generated: 2026-03-14T09:21:59.178Z
4
4
 
5
5
  This is a concise summary of all available API functions and types.
6
6
 
@@ -938,9 +938,9 @@ interface AnalyticsLocation {
938
938
  }
939
939
  ```
940
940
 
941
- **AnalyticsStandardMetadataFields** (interface)
941
+ **AnalyticsStandardEventFields** (interface)
942
942
  ```typescript
943
- interface AnalyticsStandardMetadataFields {
943
+ interface AnalyticsStandardEventFields {
944
944
  visitorId?: string
945
945
  referrer?: string
946
946
  referrerHost?: string
@@ -977,13 +977,13 @@ interface AnalyticsTrackOptions {
977
977
  ```typescript
978
978
  interface AnalyticsBrowserConfig {
979
979
  sessionStorageKey?: string
980
- sessionIdFactory?: () => string
980
+ sessionIdFactory?: () => AnalyticsSessionId
981
981
  visitorId?: string
982
982
  visitorStorage?: AnalyticsStorageMode
983
983
  visitorStorageKey?: string
984
984
  visitorIdFactory?: () => string
985
985
  autoCaptureCampaignParams?: boolean
986
- campaignParamMap?: Partial<Record<keyof AnalyticsStandardMetadataFields, string | string[]>>
986
+ campaignParamMap?: Partial<Record<keyof AnalyticsStandardEventFields, string | string[]>>
987
987
  defaultCollectionEvent?: Partial<CollectionAnalyticsEvent>
988
988
  defaultTagEvent?: Partial<TagAnalyticsEvent>
989
989
  getCollectionDefaults?: () => Partial<CollectionAnalyticsEvent> | undefined
@@ -1048,8 +1048,8 @@ interface AnalyticsFilterRequest {
1048
1048
  batchIds?: string[]
1049
1049
  variantId?: string
1050
1050
  variantIds?: string[]
1051
- sessionId?: string
1052
- sessionIds?: string[]
1051
+ sessionId?: AnalyticsSessionId
1052
+ sessionIds?: AnalyticsSessionId[]
1053
1053
  country?: string
1054
1054
  countries?: string[]
1055
1055
  metadata?: Record<string, any>
@@ -1256,6 +1256,8 @@ interface AnalyticsTagsResponse {
1256
1256
 
1257
1257
  **AnalyticsStorageMode** = `'local' | 'session' | false`
1258
1258
 
1259
+ **AnalyticsSessionId** = `number`
1260
+
1259
1261
  **EventAnalyticsDimension** = ``
1260
1262
 
1261
1263
  **TagAnalyticsDimension** = ``
@@ -5789,7 +5791,7 @@ Fire-and-forget tag analytics event. Uses `navigator.sendBeacon()` when availabl
5789
5791
  **configure**(config: AnalyticsBrowserConfig) → `void`
5790
5792
  Fire-and-forget tag analytics event. Uses `navigator.sendBeacon()` when available, falling back to `fetch(..., { keepalive: true })`.
5791
5793
 
5792
- **getSessionId**() → `string | undefined`
5794
+ **getSessionId**() → `AnalyticsSessionId | undefined`
5793
5795
  Fire-and-forget tag analytics event. Uses `navigator.sendBeacon()` when available, falling back to `fetch(..., { keepalive: true })`.
5794
5796
 
5795
5797
  **getVisitorId**() → `string | undefined`
@@ -1,26 +1,31 @@
1
1
  # Analytics Metadata Conventions
2
2
 
3
- Use these as the recommended standard keys for analytics event metadata.
3
+ Use these as the recommended standard analytics keys.
4
4
 
5
- You can put them in `metadata` directly today. The public analytics ingestion endpoints also accept these as top-level convenience fields and mirror them into `metadata` on the backend.
5
+ Some of these are now promoted top-level analytics fields. Others remain good metadata keys for custom dimensions.
6
6
 
7
7
  ---
8
8
 
9
9
  ## Recommended Keys
10
10
 
11
- ### Traffic and attribution
11
+ ### Promoted top-level fields
12
12
 
13
- - `referrer`
13
+ - `visitorId`
14
14
  - `referrerHost`
15
+ - `entryType`
16
+ - `pageId`
17
+ - `scanMethod`
18
+
19
+ These should be sent as top-level analytics fields, not inside `metadata`.
20
+
21
+ ### Metadata-friendly keys
22
+
23
+ - `referrer`
15
24
  - `utmSource`
16
25
  - `utmMedium`
17
26
  - `utmCampaign`
18
27
  - `utmContent`
19
28
  - `utmTerm`
20
- - `entryType` — for example `direct`, `qr`, `nfc`, `social`, `email`, `paid`, or `organic`
21
-
22
- ### Link-tree and page analytics
23
-
24
29
  - `group`
25
30
  - `tag`
26
31
  - `campaign`
@@ -31,13 +36,8 @@ You can put them in `metadata` directly today. The public analytics ingestion en
31
36
  - `linkTitle`
32
37
  - `destinationDomain`
33
38
  - `pagePath`
34
- - `pageId`
35
39
  - `qrCodeId`
36
40
 
37
- ### Physical scan analytics
38
-
39
- - `scanMethod` — for example `nfc` or `qr`
40
-
41
41
  ---
42
42
 
43
43
  ## Why These Matter
@@ -56,6 +56,7 @@ These keys give teams a shared vocabulary for:
56
56
 
57
57
  - Treat these as reserved standard keys.
58
58
  - Prefer these names before inventing custom alternatives.
59
+ - Send promoted fields at top level.
59
60
  - Keep values flat and scalar where possible so they are easier to filter and break down later.
60
61
  - Promote a field to a first-class backend column only when it becomes a hot platform-wide dimension.
61
62
 
@@ -65,14 +66,17 @@ These keys give teams a shared vocabulary for:
65
66
 
66
67
  ```typescript
67
68
  analytics.collection.track({
68
- sessionId: 'sess_123',
69
+ sessionId: 1234567890,
69
70
  eventType: 'click_link',
70
71
  collectionId: 'demo-collection',
72
+ visitorId: 'visitor_123',
71
73
  linkId: 'hero-cta',
72
74
  href: 'https://example.com/buy',
75
+ referrerHost: 'instagram.com',
73
76
  placement: 'hero',
74
77
  campaign: 'summer-launch',
75
78
  utmSource: 'email',
79
+ pageId: 'QR123',
76
80
  metadata: {
77
81
  pagePath: '/c/demo-collection',
78
82
  },
package/docs/analytics.md CHANGED
@@ -44,7 +44,7 @@ There are two analytics domains:
44
44
  | Collection events | `/public/analytics/collection` | Page views, clicks, app navigation, landing pages |
45
45
  | Tag events | `/public/analytics/tag` | NFC / QR scans, claim/code activity, suspicious scan monitoring |
46
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.
47
+ The backend stores custom analytics dimensions in `metadata`, but promoted analytics fields now belong at top level and are queried from real columns.
48
48
 
49
49
  See [docs/analytics-metadata-conventions.md](analytics-metadata-conventions.md) for the recommended key set.
50
50
 
@@ -60,7 +60,7 @@ import { initializeApi, analytics } from '@proveanything/smartlinks'
60
60
  initializeApi({ baseURL: 'https://smartlinks.app/api/v1' })
61
61
 
62
62
  analytics.collection.track({
63
- sessionId: 'sess_123',
63
+ sessionId: 1234567890,
64
64
  eventType: 'page_view',
65
65
  collectionId: 'demo-collection',
66
66
  productId: 'product_1',
@@ -74,7 +74,7 @@ analytics.collection.track({
74
74
 
75
75
  ```typescript
76
76
  analytics.collection.track({
77
- sessionId: 'sess_123',
77
+ sessionId: 1234567890,
78
78
  eventType: 'click_link',
79
79
  collectionId: 'demo-collection',
80
80
  productId: 'product_1',
@@ -91,7 +91,7 @@ analytics.collection.track({
91
91
 
92
92
  ```typescript
93
93
  analytics.tag.track({
94
- sessionId: 'sess_123',
94
+ sessionId: 1234567890,
95
95
  eventType: 'scan_tag',
96
96
  collectionId: 'demo-collection',
97
97
  productId: 'product_1',
@@ -298,15 +298,13 @@ Tracks generic collection analytics events such as:
298
298
  - internal navigation
299
299
  - outbound link activity
300
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.
301
+ Supported top-level fields include the core event fields plus promoted analytics columns such as `visitorId`, `referrerHost`, `pageId`, and `entryType`, along with custom metadata dimensions like `group`, `placement`, `pagePath`, and `qrCodeId`.
304
302
 
305
303
  Example:
306
304
 
307
305
  ```typescript
308
306
  analytics.collection.track({
309
- sessionId: 'sess_123',
307
+ sessionId: 1234567890,
310
308
  eventType: 'page_view',
311
309
  collectionId: 'demo-collection',
312
310
  productId: 'product_1',
@@ -322,6 +320,7 @@ analytics.collection.track({
322
320
  utmCampaign: 'summer-launch',
323
321
  group: 'summer-launch',
324
322
  placement: 'hero',
323
+ pageId: 'QR123',
325
324
  metadata: { pagePath: '/c/demo-collection?pageId=QR123' },
326
325
  })
327
326
  ```
@@ -335,15 +334,13 @@ Tracks physical scan analytics such as:
335
334
  - claim/code activity
336
335
  - admin vs customer scan behavior
337
336
 
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.
337
+ Supported top-level fields include the core scan fields plus promoted analytics columns such as `visitorId`, `entryType`, and `scanMethod`, along with custom metadata dimensions like `group`, `tag`, and campaign extras.
341
338
 
342
339
  Example:
343
340
 
344
341
  ```typescript
345
342
  analytics.tag.track({
346
- sessionId: 'sess_123',
343
+ sessionId: 1234567890,
347
344
  eventType: 'scan_tag',
348
345
  collectionId: 'demo-collection',
349
346
  productId: 'product_1',
@@ -374,6 +371,8 @@ Notes:
374
371
 
375
372
  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
373
 
374
+ Promoted fields such as `visitorId`, `referrerHost`, `pageId`, `entryType`, and `scanMethod` should be sent and queried as top-level fields, not inside `metadata`.
375
+
377
376
  ```typescript
378
377
  const grouped = await analytics.admin.breakdown('demo-collection', {
379
378
  source: 'tag',
@@ -528,7 +527,7 @@ const traffic = await analytics.admin.timeseries('demo-collection', {
528
527
  })
529
528
  ```
530
529
 
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.
530
+ `uniqueVisitors` now works in generic analytics queries. The backend uses the top-level `visitorId` column when present and falls back to numeric `sessionId` for older events that do not include it yet.
532
531
 
533
532
  ### Breakdown
534
533
 
@@ -579,7 +578,7 @@ Most admin analytics queries support combinations of:
579
578
  - `proofId` or `proofIds[]`
580
579
  - `batchId` or `batchIds[]`
581
580
  - `variantId` or `variantIds[]`
582
- - `sessionId` or `sessionIds[]`
581
+ - `sessionId` or `sessionIds[]` as numbers
583
582
  - `country` or `countries[]`
584
583
  - `metadata` for top-level JSON equality matching
585
584
 
@@ -626,11 +625,9 @@ Analytics metadata filtering currently works best with top-level scalar keys suc
626
625
  - `campaign`
627
626
  - `group`
628
627
  - `tag`
629
- - `referrerHost`
630
628
  - `utmSource`
631
629
  - `utmCampaign`
632
630
  - `pagePath`
633
- - `scanMethod`
634
631
 
635
632
  See [docs/analytics-metadata-conventions.md](analytics-metadata-conventions.md) for the recommended shared vocabulary.
636
633
 
@@ -645,7 +642,7 @@ Use `/summary`, `/timeseries`, `/breakdown`, and `/events` when you are building
645
642
  ```typescript
646
643
  function trackAndNavigate(href: string) {
647
644
  analytics.collection.track({
648
- sessionId: 'sess_123',
645
+ sessionId: 1234567890,
649
646
  eventType: 'click_link',
650
647
  collectionId: 'demo-collection',
651
648
  linkId: 'buy-now',
package/openapi.yaml CHANGED
@@ -10782,7 +10782,7 @@ components:
10782
10782
  type: number
10783
10783
  area:
10784
10784
  type: number
10785
- AnalyticsStandardMetadataFields:
10785
+ AnalyticsStandardEventFields:
10786
10786
  type: object
10787
10787
  properties:
10788
10788
  visitorId:
@@ -10834,7 +10834,7 @@ components:
10834
10834
  type: object
10835
10835
  properties:
10836
10836
  sessionId:
10837
- type: string
10837
+ $ref: "#/components/schemas/AnalyticsSessionId"
10838
10838
  eventType:
10839
10839
  $ref: "#/components/schemas/AnalyticsEventType"
10840
10840
  collectionId:
@@ -10873,7 +10873,7 @@ components:
10873
10873
  type: object
10874
10874
  properties:
10875
10875
  sessionId:
10876
- type: string
10876
+ $ref: "#/components/schemas/AnalyticsSessionId"
10877
10877
  eventType:
10878
10878
  $ref: "#/components/schemas/AnalyticsEventType"
10879
10879
  collectionId:
@@ -11048,11 +11048,11 @@ components:
11048
11048
  items:
11049
11049
  type: string
11050
11050
  sessionId:
11051
- type: string
11051
+ $ref: "#/components/schemas/AnalyticsSessionId"
11052
11052
  sessionIds:
11053
11053
  type: array
11054
11054
  items:
11055
- type: string
11055
+ $ref: "#/components/schemas/AnalyticsSessionId"
11056
11056
  country:
11057
11057
  type: string
11058
11058
  countries:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proveanything/smartlinks",
3
- "version": "1.8.0",
3
+ "version": "1.8.1",
4
4
  "description": "Official JavaScript/TypeScript SDK for the Smartlinks API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",