Skip to content

Commit d66a682

Browse files
feat(audience): add CDN bundle, demo page, and README
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 9bfbb9d commit d66a682

5 files changed

Lines changed: 531 additions & 1 deletion

File tree

packages/audience/web/README.md

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
# @imtbl/audience-web-sdk
2+
3+
Consent-aware event tracking and identity management for web surfaces. Part of the Immutable Audience platform.
4+
5+
## Install
6+
7+
```bash
8+
npm install @imtbl/audience-web-sdk
9+
```
10+
11+
## Quick Start
12+
13+
```typescript
14+
import { ImmutableWebSDK } from '@imtbl/audience-web-sdk';
15+
16+
const sdk = ImmutableWebSDK.init({
17+
publishableKey: 'pk_imtbl_...',
18+
environment: 'production',
19+
consent: 'anonymous',
20+
});
21+
22+
sdk.track('purchase', { currency: 'USD', value: 9.99, itemId: 'sword_01' });
23+
sdk.page();
24+
```
25+
26+
## Initialisation
27+
28+
```typescript
29+
const sdk = ImmutableWebSDK.init({
30+
publishableKey: 'pk_imtbl_...', // Required — from Immutable Hub
31+
environment: 'production', // 'dev' | 'sandbox' | 'production'
32+
consent: 'none', // 'none' | 'anonymous' | 'full' (default: 'none')
33+
consentSource: 'CookieBannerV2', // Identifies the consent source (default: 'WebSDK')
34+
debug: false, // Log all events to console (default: false)
35+
cookieDomain: '.studio.com', // Cross-subdomain cookie sharing (optional)
36+
flushInterval: 5000, // Queue flush interval in ms (default: 5000)
37+
flushSize: 20, // Queue flush size threshold (default: 20)
38+
});
39+
```
40+
41+
## Consent
42+
43+
The SDK defaults to `none` — no events are collected until consent is explicitly set.
44+
45+
```typescript
46+
sdk.setConsent('anonymous'); // Anonymous tracking (no PII)
47+
sdk.setConsent('full'); // Full tracking (PII via identify)
48+
sdk.setConsent('none'); // Stop tracking, purge queue, clear cookies
49+
```
50+
51+
Consent is:
52+
- **Set at init** via the `consent` config option
53+
- **Synced server-side** via `PUT /v1/audience/tracking-consent`
54+
- **Reconciled on init** via `GET /v1/audience/tracking-consent` (restores consent if config is stale)
55+
56+
| Level | Behaviour |
57+
|-------|-----------|
58+
| `none` | SDK is inert. No events collected. Queue purged on downgrade. |
59+
| `anonymous` | Events collected with anonymous ID only. `identify()` calls are discarded. |
60+
| `full` | Full collection. `identify()` sends. `userId` included on events. |
61+
62+
**On downgrade to `none`:** queue purged, `imtbl_anon_id` and `_imtbl_sid` cookies cleared.
63+
**On downgrade from `full` to `anonymous`:** identify messages purged, `userId` stripped from queued events.
64+
65+
## Auto-Tracked Events
66+
67+
The SDK automatically fires these events. Studios do not call them.
68+
69+
| Event | When | Properties |
70+
|-------|------|------------|
71+
| `session_start` | SDK init with no active session cookie | `sessionId` |
72+
| `session_end` | `shutdown()` called | `sessionId`, `duration` (seconds) |
73+
74+
## Event Tracking
75+
76+
```typescript
77+
sdk.track('sign_up', { method: 'google' });
78+
sdk.track('purchase', { currency: 'USD', value: 9.99 });
79+
sdk.track('wishlist_add', { gameId: 'game_123', source: 'landing_page' });
80+
sdk.track('beta_key_redeemed', { source: 'influencer' });
81+
```
82+
83+
## Page Tracking
84+
85+
Call `sdk.page()` on route changes. Attribution context (UTMs, click IDs, referrer, landing page) is automatically attached to the first page view.
86+
87+
```typescript
88+
sdk.page();
89+
sdk.page({ section: 'shop', category: 'weapons' });
90+
```
91+
92+
## Identity
93+
94+
```typescript
95+
// Identify a known user (requires full consent)
96+
sdk.identify('user@example.com', 'email');
97+
sdk.identify('76561198012345', 'steam');
98+
sdk.identify('passport_sub_abc', 'passport', {
99+
email: 'user@example.com',
100+
name: 'Player One',
101+
});
102+
103+
// Identify with traits only (anonymous, no userId)
104+
sdk.identify({ source: 'steam', steamId: '76561198012345' });
105+
106+
// Link two identities (same player, different providers)
107+
sdk.alias(
108+
{ uid: '76561198012345', provider: 'steam' },
109+
{ uid: 'user@example.com', provider: 'email' },
110+
);
111+
112+
// Reset on logout (new anonymous ID, clears userId)
113+
sdk.reset();
114+
```
115+
116+
## Queue & Lifecycle
117+
118+
```typescript
119+
await sdk.flush(); // Force flush all queued events
120+
sdk.shutdown(); // Flush remaining events, stop the SDK
121+
```
122+
123+
Events are batched and flushed every 5 seconds or when 20 messages accumulate. On page unload (`visibilitychange` / `pagehide`), remaining events are flushed via `fetch` with `keepalive: true`.
124+
125+
## CDN Usage
126+
127+
For sites without a bundler:
128+
129+
```html
130+
<script src="https://cdn.immutable.com/web-sdk/v1/imtbl-web.js"></script>
131+
<script>
132+
var sdk = window.ImmutableWebSDK.init({
133+
publishableKey: 'pk_imtbl_...',
134+
environment: 'production',
135+
consent: 'anonymous',
136+
});
137+
138+
sdk.track('signup_started');
139+
sdk.identify('user@example.com', 'email');
140+
</script>
141+
```
142+
143+
## Cookies
144+
145+
All cookies are first-party, `SameSite=Lax`, `Secure` on HTTPS, and shared with the pixel:
146+
147+
| Cookie | Lifetime | Purpose |
148+
|--------|----------|---------|
149+
| `imtbl_anon_id` | 2 years | Anonymous device ID |
150+
| `_imtbl_sid` | 30 min (rolling) | Session continuity |
151+
152+
## Wire Format
153+
154+
Events are sent to `POST /v1/audience/messages` with the `x-immutable-publishable-key` header. All messages include `surface: 'web'` and follow the backend OpenAPI spec.

0 commit comments

Comments
 (0)