@returningai/widget-sdk 1.2.14 → 1.2.15

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 CHANGED
@@ -1,575 +1,527 @@
1
- # @returningai/widget-sdk
2
-
3
- Embed ReturningAI widgets on any website — React, Vue, Angular, or plain `<script>` tag. No external dependencies.
4
-
5
- ---
6
-
7
- ## Quick Start
8
-
9
- ### 1. Get your credentials
10
-
11
- Log in to your **ReturningAI dashboard** and go to **Community Settings > Integrations > SDK Access**. You will find:
12
-
13
- - **Widget ID** — identifies your widget
14
- - **API URL** — the base URL for widget authentication
15
- - **Widget URL** — the URL that loads the widget content
16
-
17
- ### 2. Add the SDK to your page
18
-
19
- ```html
20
- <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
21
- <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
22
- data-widget-id="YOUR_WIDGET_ID"
23
- data-widget-type="store"
24
- data-theme="dark"
25
- data-email="user@example.com"
26
- api-url="YOUR_API_URL"
27
- widget-url="YOUR_WIDGET_URL"
28
- ></script>
29
- ```
30
-
31
- That's it. The widget will authenticate and render automatically.
32
-
33
- ---
34
-
35
- ## Embed Modes
36
-
37
- Choose an embed mode based on your setup:
38
-
39
- | | Public Embed | Access Key Embed | Authenticated Embed |
40
- |---|---|---|---|
41
- | **Backend required** | **No** | Yes | Yes |
42
- | **Security** | Basic | High | Highest |
43
- | **Best for** | Quick setup, demos | Production websites | Apps with user login |
44
-
45
- ### Public Embed (no backend needed)
46
-
47
- The simplest way to embed a widget. The SDK handles everything — no server-side code required.
48
-
49
- ```html
50
- <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
51
- <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
52
- data-widget-id="YOUR_WIDGET_ID"
53
- data-widget-type="store"
54
- data-theme="dark"
55
- data-email="user@example.com"
56
- api-url="YOUR_API_URL"
57
- widget-url="YOUR_WIDGET_URL"
58
- ></script>
59
- ```
60
-
61
- The `data-email` attribute identifies the user. The SDK authenticates directly with the ReturningAI backend on the client side.
62
-
63
- **When to use:** Demos, internal tools, or any scenario where you don't have a backend server.
64
-
65
- ### Access Key Embed (recommended for production)
66
-
67
- Your access credentials stay on your server. Your server exchanges them for a **short-lived embed token (15 min)** and injects only that token into the page. No secret ever reaches the browser.
68
-
69
- **Step 1 — Create an access key pair** (one-time, from your dashboard):
70
-
71
- Go to **Community Settings > Integrations > SDK Access** and click **Create SDK Key**. Enter a name and optional allowed origins.
72
-
73
- The dashboard will display your `accessId` and `accessKey`. The `accessKey` is shown **once only** — save it securely.
74
-
75
- Store both values in your server environment variables.
76
-
77
- **Step 2 — Exchange credentials for an embed token on every page load** (Node.js example):
78
-
79
- ```js
80
- // Runs on YOUR SERVER — never in the browser
81
- const response = await fetch('https://YOUR_API_URL/v2/api/widget-access-keys/token', {
82
- method: 'POST',
83
- headers: { 'Content-Type': 'application/json' },
84
- body: JSON.stringify({
85
- accessId: process.env.RAI_ACCESS_ID,
86
- accessKey: process.env.RAI_ACCESS_KEY,
87
- }),
88
- })
89
- const { data } = await response.json()
90
- // data.embedToken expires in 15 minutes
91
- ```
92
-
93
- **Step 3 — Embed** (only the short-lived token appears in HTML):
94
-
95
- ```html
96
- <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
97
- <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
98
- data-widget-id="YOUR_WIDGET_ID"
99
- data-widget-type="store"
100
- data-theme="dark"
101
- data-email="user@example.com"
102
- data-embed-token="eyJ..."
103
- api-url="YOUR_API_URL"
104
- widget-url="YOUR_WIDGET_URL"
105
- ></script>
106
- ```
107
-
108
- The SDK validates the token on widget load. If the token is expired or invalid, the widget shows an error screen. Tokens expire after 15 minutes — regenerate on each page render.
109
-
110
- ### Authenticated Embed (custom backend auth)
111
-
112
- Your backend holds the API key. The SDK calls your endpoint to get a token — the API key never touches the browser.
113
-
114
- **Step 1 — Create a backend endpoint** (Node.js example):
115
-
116
- ```js
117
- app.post('/api/widget-auth', async (req, res) => {
118
- const response = await fetch('https://YOUR_API_URL/{community_id}/auth/signin', {
119
- method: 'POST',
120
- headers: {
121
- 'returningai-api-key': process.env.WIDGET_API_KEY,
122
- 'email': req.user.email,
123
- 'Content-Type': 'application/json',
124
- },
125
- })
126
- const data = await response.json()
127
- res.json({ token: data.token }) // never forward the API key
128
- })
129
- ```
130
-
131
- The endpoint must return `{ token }`. Optionally include `expiresIn` (seconds) and the SDK will schedule a re-auth before expiry — otherwise it defaults to 300 seconds.
132
-
133
- **Step 2 — Embed**:
134
-
135
- ```html
136
- <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
137
- <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
138
- data-widget-id="YOUR_WIDGET_ID"
139
- data-widget-type="store"
140
- data-theme="dark"
141
- data-auth-url="/api/widget-auth"
142
- api-url="YOUR_API_URL"
143
- widget-url="YOUR_WIDGET_URL"
144
- ></script>
145
- ```
146
-
147
- No `data-email` needed — user identity is resolved on your backend.
148
-
149
- ---
150
-
151
- ## Install
152
-
153
- ### CDN (no install needed)
154
-
155
- ```html
156
- <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"></script>
157
- ```
158
-
159
- ### npm / yarn / pnpm
160
-
161
- ```bash
162
- npm install @returningai/widget-sdk
163
- ```
164
-
165
- ---
166
-
167
- ## Widget Types
168
-
169
- Set the `data-widget-type` attribute to choose which widget to display. The Widget ID and Widget URL for each type are available in your dashboard.
170
-
171
- | `data-widget-type` | Description |
172
- |---------------------|-------------|
173
- | `store` | Store / rewards widget |
174
- | `channel` | Channel widget |
175
- | `milestone` | Milestone widget |
176
- | `social` | Social widget |
177
- | `currency-view` | Currency widget |
178
- | `referral-conditions` | Referral widget |
179
- | `custom` | Custom widget |
180
-
181
- ### Store widget
182
-
183
- ```html
184
- <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
185
- <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
186
- data-widget-id="YOUR_WIDGET_ID"
187
- data-widget-type="store"
188
- data-theme="dark"
189
- data-email="user@example.com"
190
- api-url="YOUR_API_URL"
191
- widget-url="YOUR_WIDGET_URL"
192
- ></script>
193
- ```
194
-
195
- ### Channel widget
196
-
197
- ```html
198
- <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
199
- <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
200
- data-widget-id="YOUR_WIDGET_ID"
201
- data-widget-type="channel"
202
- data-theme="dark"
203
- data-email="user@example.com"
204
- api-url="YOUR_API_URL"
205
- widget-url="YOUR_WIDGET_URL"
206
- ></script>
207
- ```
208
-
209
- > **Note:** `YOUR_WIDGET_ID` for channel widgets is the base64-encoded channel ID — find it under **Community Settings > Channels** in your dashboard. Alternatively, use the [custom element approach](#framework-usage-custom-elements) with separate `community-id` and `channel-id` attributes.
210
-
211
- ### Social widget
212
-
213
- ```html
214
- <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
215
- <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
216
- data-widget-id="YOUR_WIDGET_ID"
217
- data-widget-type="social"
218
- data-theme="dark"
219
- data-email="user@example.com"
220
- api-url="YOUR_API_URL"
221
- widget-url="YOUR_WIDGET_URL"
222
- ></script>
223
- ```
224
-
225
- ### Milestone widget
226
-
227
- ```html
228
- <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
229
- <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
230
- data-widget-id="YOUR_WIDGET_ID"
231
- data-widget-type="milestone"
232
- data-theme="dark"
233
- data-email="user@example.com"
234
- api-url="YOUR_API_URL"
235
- widget-url="YOUR_WIDGET_URL"
236
- ></script>
237
- ```
238
-
239
- ### Custom widget
240
-
241
- ```html
242
- <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
243
- <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
244
- data-widget-id="YOUR_WIDGET_ID"
245
- data-widget-type="custom"
246
- data-theme="dark"
247
- data-email="user@example.com"
248
- api-url="YOUR_API_URL"
249
- widget-url="YOUR_WIDGET_URL"
250
- ></script>
251
- ```
252
-
253
- ---
254
-
255
- ## Attributes
256
-
257
- There are two ways to identify your widget:
258
-
259
- 1. **Script-tag embed:** Use `data-widget-id` (a pre-encoded ID from your dashboard) on the `<script>` tag
260
- 2. **Custom element embed:** Use `community-id` (and `channel-id` for channel widgets) on the custom element tag
261
-
262
- All attributes can be provided with or without the `data-` prefix — both work.
263
-
264
- ### Script-tag attributes
265
-
266
- | Attribute | Required | Default | Description |
267
- |-----------|----------|---------|-------------|
268
- | `data-widget-id` | **Yes** | — | Your widget ID (from dashboard) |
269
- | `data-widget-type` | **Yes** | `store` | Widget type (see table above) |
270
- | `api-url` | **Yes**\* | — | Auth API base URL (from dashboard) |
271
- | `widget-url` | **Yes**\* | — | Widget URL (from dashboard). \*Not required when using `data-bundle-url` |
272
- | `data-bundle-url` | No | — | URL to the widget IIFE bundle — triggers [bundle mode](#bundle-mode) (non-iframe) |
273
- | `data-domain-key` | Bundle mode | — | Environment key: `SGTR`, `STG`, or `GEN`. Required for bundle mode on external sites |
274
- | `data-theme` | No | `light` | `light` or `dark` |
275
- | `data-email` | Recommended | — | User's email for identification |
276
- | `data-embed-token` | Access Key only | — | Short-lived token from your server |
277
- | `data-auth-url` | Auth Embed only | — | Your backend auth endpoint |
278
- | `data-locale` | No | — | BCP 47 locale tag (e.g. `fr-FR`) |
279
- | `data-eager` | No | — | Load immediately instead of waiting until visible |
280
- | `data-auto-refresh` | No | `true` | Auto-refresh access token before expiry |
281
- | `data-debug` | No | `false` | Verbose console logging |
282
- | `data-custom-data` | No | — | JSON object forwarded to the widget |
283
-
284
- ### Custom element attributes
285
-
286
- | Attribute | Required | Default | Description |
287
- |-----------|----------|---------|-------------|
288
- | `community-id` | **Yes**\* | — | Your community ID (raw ObjectId from dashboard). \*Custom widgets use `widget-id` instead |
289
- | `channel-id` | **Channel only** | — | Channel ID (raw ObjectId) — required for `<rai-channel-widget>` |
290
- | `api-url` | **Yes** | — | Auth API base URL (from dashboard) |
291
- | `widget-url` | **Yes**\* | — | Widget URL (from dashboard). \*Not required when using `bundle-url` |
292
- | `bundle-url` | No | — | URL to the widget IIFE bundle — triggers [bundle mode](#bundle-mode) (non-iframe) |
293
- | `domain-key` | Bundle mode | — | Environment key: `SGTR`, `STG`, or `GEN`. Required for bundle mode on external sites |
294
- | `theme` | No | `light` | `light` or `dark` |
295
- | `data-email` | Recommended | — | User's email for identification |
296
- | `embed-token` | Access Key only | — | Short-lived token from your server |
297
- | `auth-url` | Auth Embed only | — | Your backend auth endpoint |
298
- | `width` | No | `100%` | CSS width |
299
- | `height` | No | `600px` | CSS height |
300
- | `locale` | No | — | BCP 47 locale tag (e.g. `fr-FR`) |
301
- | `eager` | No | — | Load immediately instead of waiting until visible |
302
- | `auto-refresh` | No | `true` | Auto-refresh access token before expiry |
303
- | `debug` | No | `false` | Verbose console logging |
304
- | `custom-data` | No | — | JSON object forwarded to the widget |
305
-
306
- > **`widget-id` vs `community-id`:** The script-tag method uses `data-widget-id` which is a pre-encoded identifier (base64). The custom element method uses raw ObjectIds — `community-id` for most widget types, plus `channel-id` for channel widgets. The SDK handles the encoding internally.
307
-
308
- ### Container `<div>` (script-tag method only)
309
-
310
- The SDK looks for a `<div>` with `id="returning-ai-widget-{YOUR_WIDGET_ID}"`. Size the div to control the widget dimensions:
311
-
312
- ```html
313
- <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
314
- ```
315
-
316
- Custom elements don't need a container div — they size themselves using `width` and `height` attributes (defaults: `100%` and `600px`).
317
-
318
- ### User Identifier Attributes
319
-
320
- Pass any `data-*` attribute to identify the current user. Which identifiers are available is configured per community in your dashboard.
321
-
322
- ```html
323
- <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
324
- data-widget-id="YOUR_WIDGET_ID"
325
- data-widget-type="store"
326
- data-email="user@example.com"
327
- data-user-id="12345"
328
- data-account-id="abc"
329
- api-url="YOUR_API_URL"
330
- widget-url="YOUR_WIDGET_URL"
331
- ></script>
332
- ```
333
-
334
- Any `data-*` attribute not reserved by the SDK is forwarded to the auth API as a user identifier.
335
-
336
- ---
337
-
338
- ## Bundle Mode
339
-
340
- When you set the `bundle-url` attribute, the widget renders directly in the page DOM instead of inside an iframe. This gives full CSS cascade — `html[data-theme]` and Tailwind classes apply to the widget content naturally.
341
-
342
- `widget-url` is not needed in bundle mode.
343
-
344
- ```html
345
- <!-- Script-tag approach -->
346
- <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:100vh"></div>
347
- <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
348
- data-widget-id="YOUR_WIDGET_ID"
349
- data-widget-type="store"
350
- data-theme="dark"
351
- data-email="user@example.com"
352
- data-bundle-url="YOUR_BUNDLE_URL"
353
- api-url="YOUR_API_URL"
354
- data-eager
355
- ></script>
356
-
357
- <!-- Custom element approach -->
358
- <rai-store-widget
359
- community-id="YOUR_COMMUNITY_ID"
360
- api-url="YOUR_API_URL"
361
- bundle-url="YOUR_BUNDLE_URL"
362
- domain-key="YOUR_DOMAIN_KEY"
363
- data-email="user@example.com"
364
- theme="dark"
365
- width="100%"
366
- height="100vh"
367
- eager
368
- ></rai-store-widget>
369
- ```
370
-
371
- Each widget type has its own IIFE bundle and global name:
372
-
373
- | Widget type | Bundle global | Custom element tag |
374
- |-------------|--------------|-------------------|
375
- | `store` | `RaiStoreWidget` | `<rai-store-widget>` |
376
- | `channel` | `RaiChannelWidget` | `<rai-channel-widget>` |
377
- | `social` | `RaiSocialWidget` | `<rai-social-widget>` |
378
- | `milestone` | `RaiMilestoneWidget` | `<rai-milestone-widget>` |
379
- | `currency-view` | `RaiCurrencyWidget` | `<rai-currency-widget>` |
380
- | `referral-conditions` | `RaiReferralWidget` | `<rai-referral-widget>` |
381
- | `custom` | `RaiCustomWidget` | `<rai-custom-widget>` |
382
-
383
- The bundle must export a `mount(container, config)` function on its global (e.g. `window.RaiStoreWidget.mount`).
384
-
385
- ---
386
-
387
- ## Framework Usage (Custom Elements)
388
-
389
- When using a frontend framework (React, Vue, Angular), you can use custom HTML element tags instead of the script-tag method. Custom elements use raw IDs (`community-id`, `channel-id`) instead of the pre-encoded `data-widget-id`.
390
-
391
- ### Plain HTML
392
-
393
- ```html
394
- <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"></script>
395
-
396
- <!-- Store widget -->
397
- <rai-store-widget
398
- community-id="YOUR_COMMUNITY_ID"
399
- theme="dark"
400
- data-email="user@example.com"
401
- api-url="YOUR_API_URL"
402
- widget-url="YOUR_WIDGET_URL"
403
- ></rai-store-widget>
404
-
405
- <!-- Channel widget — requires both community-id AND channel-id -->
406
- <rai-channel-widget
407
- community-id="YOUR_COMMUNITY_ID"
408
- channel-id="YOUR_CHANNEL_ID"
409
- theme="dark"
410
- data-email="user@example.com"
411
- api-url="YOUR_API_URL"
412
- widget-url="YOUR_WIDGET_URL"
413
- ></rai-channel-widget>
414
- ```
415
-
416
- ### React (TypeScript)
417
-
418
- Types are included — no manual JSX declarations needed.
419
-
420
- ```tsx
421
- import '@returningai/widget-sdk'
422
-
423
- export function StoreEmbed({ email }: { email: string }) {
424
- return (
425
- <rai-store-widget
426
- community-id="YOUR_COMMUNITY_ID"
427
- theme="dark"
428
- data-email={email}
429
- api-url="YOUR_API_URL"
430
- widget-url="YOUR_WIDGET_URL"
431
- />
432
- )
433
- }
434
-
435
- export function ChannelEmbed({ email }: { email: string }) {
436
- return (
437
- <rai-channel-widget
438
- community-id="YOUR_COMMUNITY_ID"
439
- channel-id="YOUR_CHANNEL_ID"
440
- theme="dark"
441
- data-email={email}
442
- api-url="YOUR_API_URL"
443
- widget-url="YOUR_WIDGET_URL"
444
- />
445
- )
446
- }
447
- ```
448
-
449
- ### Vue 3
450
-
451
- ```vue
452
- <script setup>
453
- import '@returningai/widget-sdk'
454
- </script>
455
-
456
- <template>
457
- <!-- Store widget -->
458
- <rai-store-widget
459
- community-id="YOUR_COMMUNITY_ID"
460
- theme="dark"
461
- :data-email="user.email"
462
- api-url="YOUR_API_URL"
463
- widget-url="YOUR_WIDGET_URL"
464
- />
465
-
466
- <!-- Channel widget -->
467
- <rai-channel-widget
468
- community-id="YOUR_COMMUNITY_ID"
469
- channel-id="YOUR_CHANNEL_ID"
470
- theme="dark"
471
- :data-email="user.email"
472
- api-url="YOUR_API_URL"
473
- widget-url="YOUR_WIDGET_URL"
474
- />
475
- </template>
476
- ```
477
-
478
- ### Angular
479
-
480
- ```ts
481
- // app.module.ts
482
- import '@returningai/widget-sdk'
483
- import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'
484
-
485
- @NgModule({ schemas: [CUSTOM_ELEMENTS_SCHEMA] })
486
- ```
487
-
488
- ```html
489
- <!-- Store widget -->
490
- <rai-store-widget
491
- community-id="YOUR_COMMUNITY_ID"
492
- theme="dark"
493
- [attr.data-email]="user.email"
494
- api-url="YOUR_API_URL"
495
- widget-url="YOUR_WIDGET_URL"
496
- ></rai-store-widget>
497
-
498
- <!-- Channel widget -->
499
- <rai-channel-widget
500
- community-id="YOUR_COMMUNITY_ID"
501
- channel-id="YOUR_CHANNEL_ID"
502
- theme="dark"
503
- [attr.data-email]="user.email"
504
- api-url="YOUR_API_URL"
505
- widget-url="YOUR_WIDGET_URL"
506
- ></rai-channel-widget>
507
- ```
508
-
509
- ### Available custom element tags
510
-
511
- | Tag | Required attributes | Widget type |
512
- |-----|-------------------|-------------|
513
- | `<rai-store-widget>` | `community-id` | Store |
514
- | `<rai-channel-widget>` | `community-id` + `channel-id` | Channel |
515
- | `<rai-milestone-widget>` | `community-id` | Milestone |
516
- | `<rai-social-widget>` | `community-id` | Social |
517
- | `<rai-currency-widget>` | `community-id` | Currency |
518
- | `<rai-referral-widget>` | `community-id` | Referral |
519
- | `<rai-custom-widget>` | `widget-id` (not `community-id`) | Custom |
520
-
521
- ---
522
-
523
- ## DOM Events
524
-
525
- Listen for widget lifecycle events:
526
-
527
- | Event | `detail` | Fired when |
528
- |-------|----------|-----------|
529
- | `rai-authenticated` | `{}` | Auth succeeded, before widget mounts |
530
- | `rai-ready` | `{}` | `WIDGET_READY` received, loader hidden (iframe mode) |
531
- | `rai-mounted` | `{}` | Widget bundle mounted successfully (bundle mode) |
532
- | `rai-error` | `{ message }` | Auth failed after all retries |
533
- | `rai-logout` | `{}` | Widget logged out |
534
- | `rai-height-change` | `{ height }` | iframe resized (after debounce) |
535
-
536
- ```js
537
- // Script-tag embed
538
- const widget = document.querySelector('[data-widget-id]')
539
-
540
- // Custom element embed
541
- const widget = document.querySelector('rai-store-widget')
542
-
543
- widget.addEventListener('rai-authenticated', () => { /* auth succeeded */ })
544
- widget.addEventListener('rai-ready', () => { /* widget loaded and visible (iframe mode) */ })
545
- widget.addEventListener('rai-mounted', () => { /* widget mounted (bundle mode) */ })
546
- widget.addEventListener('rai-error', (e) => console.error(e.detail.message))
547
- widget.addEventListener('rai-logout', () => { /* user logged out */ })
548
- widget.addEventListener('rai-height-change', (e) => console.log(e.detail.height))
549
- ```
550
-
551
- ---
552
-
553
- ## Public API
554
-
555
- After the SDK loads, `window.ReturningAIWidget` is available:
556
-
557
- ```js
558
- window.ReturningAIWidget.version // current SDK version
559
- await window.ReturningAIWidget.reload() // re-runs auth flow and reloads widget
560
- await window.ReturningAIWidget.logout() // clears tokens and removes widget
561
- window.ReturningAIWidget.isAuthenticated() // boolean
562
- window.ReturningAIWidget.getTokenInfo() // token metadata (no token values exposed)
563
- ```
564
-
565
- ---
566
-
567
- ## Browser Support
568
-
569
- Requires Custom Elements v1 + Shadow DOM v1.
570
-
571
- | Browser | Minimum version |
572
- |---------|----------------|
573
- | Chrome | 67 |
574
- | Firefox | 63 |
575
- | Safari | 13 |
1
+ # @returningai/widget-sdk
2
+
3
+ Embed ReturningAI widgets on any website — React, Vue, Angular, or plain `<script>` tag. No external dependencies.
4
+
5
+ ---
6
+
7
+ ## Quick Start
8
+
9
+ ### 1. Get your credentials
10
+
11
+ Log in to your **ReturningAI dashboard** and go to **Community Settings > Integrations > SDK Access**. You will find:
12
+
13
+ - **Widget ID** — identifies your widget
14
+ - **API URL** — the base URL for widget authentication
15
+ - **Widget URL** — the URL that loads the widget content
16
+
17
+ ### 2. Add the SDK to your page
18
+
19
+ ```html
20
+ <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
21
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
22
+ data-widget-id="YOUR_WIDGET_ID"
23
+ data-widget-type="store"
24
+ data-theme="dark"
25
+ data-email="user@example.com"
26
+ api-url="YOUR_API_URL"
27
+ widget-url="YOUR_WIDGET_URL"
28
+ ></script>
29
+ ```
30
+
31
+ That's it. The widget will authenticate and render automatically.
32
+
33
+ ---
34
+
35
+ ## Authentication
36
+
37
+ ### Public Embed (no backend needed)
38
+
39
+ The simplest way to embed a widget. The SDK handles everything — no server-side code required.
40
+
41
+ ```html
42
+ <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
43
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
44
+ data-widget-id="YOUR_WIDGET_ID"
45
+ data-widget-type="store"
46
+ data-theme="dark"
47
+ data-email="user@example.com"
48
+ api-url="YOUR_API_URL"
49
+ widget-url="YOUR_WIDGET_URL"
50
+ ></script>
51
+ ```
52
+
53
+ The `data-email` attribute identifies the user. The SDK authenticates directly with the ReturningAI backend on the client side.
54
+
55
+ **When to use:** Demos, internal tools, or any scenario where you don't have a backend server.
56
+
57
+ ### Access Key Embed (recommended for production)
58
+
59
+ Your access credentials stay on your server. Your server exchanges them for a **short-lived embed token (15 min)** and injects only that token into the page. No secret ever reaches the browser.
60
+
61
+ **Step 1 — Create an access key pair** (one-time, from your dashboard):
62
+
63
+ Go to **Community Settings > Integrations > SDK Access** and click **Create SDK Key**. Enter a name and optional allowed origins.
64
+
65
+ The dashboard will display your `accessId` and `accessKey`. The `accessKey` is shown **once only** — save it securely.
66
+
67
+ Store both values in your server environment variables.
68
+
69
+ **Step 2 — Exchange credentials for an embed token on every page load** (Node.js example):
70
+
71
+ ```js
72
+ // Runs on YOUR SERVER — never in the browser
73
+ const response = await fetch('https://YOUR_API_URL/v2/api/widget-access-keys/token', {
74
+ method: 'POST',
75
+ headers: { 'Content-Type': 'application/json' },
76
+ body: JSON.stringify({
77
+ accessId: process.env.RAI_ACCESS_ID,
78
+ accessKey: process.env.RAI_ACCESS_KEY,
79
+ }),
80
+ })
81
+ const { data } = await response.json()
82
+ // data.embedToken expires in 15 minutes
83
+ ```
84
+
85
+ **Step 3 — Embed** (only the short-lived token appears in HTML):
86
+
87
+ ```html
88
+ <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
89
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
90
+ data-widget-id="YOUR_WIDGET_ID"
91
+ data-widget-type="store"
92
+ data-theme="dark"
93
+ data-email="user@example.com"
94
+ data-embed-token="eyJ..."
95
+ api-url="YOUR_API_URL"
96
+ widget-url="YOUR_WIDGET_URL"
97
+ ></script>
98
+ ```
99
+
100
+ The SDK validates the token on widget load. If the token is expired or invalid, the widget shows an error screen. Tokens expire after 15 minutes — regenerate on each page render.
101
+
102
+ ---
103
+
104
+ ## Install
105
+
106
+ ### CDN (no install needed)
107
+
108
+ ```html
109
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"></script>
110
+ ```
111
+
112
+ ### npm / yarn / pnpm
113
+
114
+ ```bash
115
+ npm install @returningai/widget-sdk
116
+ ```
117
+
118
+ ---
119
+
120
+ ## Widget Types
121
+
122
+ Set the `data-widget-type` attribute to choose which widget to display. The Widget ID and Widget URL for each type are available in your dashboard.
123
+
124
+ | `data-widget-type` | Description |
125
+ |---------------------|-------------|
126
+ | `store` | Store / rewards widget |
127
+ | `channel` | Channel widget |
128
+ | `milestone` | Milestone widget |
129
+ | `social` | Social widget |
130
+ | `currency-view` | Currency widget |
131
+ | `referral-conditions` | Referral widget |
132
+ | `custom` | Custom widget |
133
+
134
+ ### Store widget
135
+
136
+ ```html
137
+ <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
138
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
139
+ data-widget-id="YOUR_WIDGET_ID"
140
+ data-widget-type="store"
141
+ data-theme="dark"
142
+ data-email="user@example.com"
143
+ api-url="YOUR_API_URL"
144
+ widget-url="YOUR_WIDGET_URL"
145
+ ></script>
146
+ ```
147
+
148
+ ### Channel widget
149
+
150
+ ```html
151
+ <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
152
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
153
+ data-widget-id="YOUR_WIDGET_ID"
154
+ data-widget-type="channel"
155
+ data-theme="dark"
156
+ data-email="user@example.com"
157
+ api-url="YOUR_API_URL"
158
+ widget-url="YOUR_WIDGET_URL"
159
+ ></script>
160
+ ```
161
+
162
+ > **Note:** `YOUR_WIDGET_ID` for channel widgets is the base64-encoded channel ID — find it under **Community Settings > Channels** in your dashboard. Alternatively, use the [custom element approach](#framework-usage-custom-elements) with separate `community-id` and `channel-id` attributes.
163
+
164
+ ### Social widget
165
+
166
+ ```html
167
+ <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
168
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
169
+ data-widget-id="YOUR_WIDGET_ID"
170
+ data-widget-type="social"
171
+ data-theme="dark"
172
+ data-email="user@example.com"
173
+ api-url="YOUR_API_URL"
174
+ widget-url="YOUR_WIDGET_URL"
175
+ ></script>
176
+ ```
177
+
178
+ ### Milestone widget
179
+
180
+ ```html
181
+ <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
182
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
183
+ data-widget-id="YOUR_WIDGET_ID"
184
+ data-widget-type="milestone"
185
+ data-theme="dark"
186
+ data-email="user@example.com"
187
+ api-url="YOUR_API_URL"
188
+ widget-url="YOUR_WIDGET_URL"
189
+ ></script>
190
+ ```
191
+
192
+ ### Custom widget
193
+
194
+ ```html
195
+ <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
196
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
197
+ data-widget-id="YOUR_WIDGET_ID"
198
+ data-widget-type="custom"
199
+ data-theme="dark"
200
+ data-email="user@example.com"
201
+ api-url="YOUR_API_URL"
202
+ widget-url="YOUR_WIDGET_URL"
203
+ ></script>
204
+ ```
205
+
206
+ ---
207
+
208
+ ## Attributes
209
+
210
+ There are two ways to identify your widget:
211
+
212
+ 1. **Script-tag embed:** Use `data-widget-id` (a pre-encoded ID from your dashboard) on the `<script>` tag
213
+ 2. **Custom element embed:** Use `community-id` (and `channel-id` for channel widgets) on the custom element tag
214
+
215
+ All attributes can be provided with or without the `data-` prefix — both work.
216
+
217
+ ### Script-tag attributes
218
+
219
+ | Attribute | Required | Default | Description |
220
+ |-----------|----------|---------|-------------|
221
+ | `data-widget-id` | **Yes** | — | Your widget ID (from dashboard) |
222
+ | `data-widget-type` | **Yes** | `store` | Widget type (see table above) |
223
+ | `api-url` | **Yes**\* | — | Auth API base URL (from dashboard) |
224
+ | `widget-url` | **Yes**\* | — | Widget URL (from dashboard). \*Not required when using `data-bundle-url` |
225
+ | `data-bundle-url` | No | — | URL to the widget IIFE bundle — triggers [bundle mode](#bundle-mode) (non-iframe) |
226
+ | `data-domain-key` | Bundle mode | — | Environment key: `SGTR`, `STG`, or `PROD`. Required for bundle mode on external sites |
227
+ | `data-theme` | No | `light` | `light` or `dark` |
228
+ | `data-email` | Recommended | — | User's email for identification |
229
+ | `data-embed-token` | Access Key only | — | Short-lived token from your server |
230
+ | `data-locale` | No | — | BCP 47 locale tag (e.g. `fr-FR`) |
231
+ | `data-eager` | No | — | Load immediately instead of waiting until visible |
232
+ | `data-auto-refresh` | No | `true` | Auto-refresh access token before expiry |
233
+ | `data-debug` | No | `false` | Verbose console logging |
234
+ | `data-custom-data` | No | — | JSON object forwarded to the widget |
235
+
236
+ ### Custom element attributes
237
+
238
+ | Attribute | Required | Default | Description |
239
+ |-----------|----------|---------|-------------|
240
+ | `community-id` | **Yes**\* | — | Your community ID (raw ObjectId from dashboard). \*Custom widgets use `widget-id` instead |
241
+ | `channel-id` | **Channel only** | — | Channel ID (raw ObjectId) — required for `<rai-channel-widget>` |
242
+ | `api-url` | **Yes** | — | Auth API base URL (from dashboard) |
243
+ | `widget-url` | **Yes**\* | — | Widget URL (from dashboard). \*Not required when using `bundle-url` |
244
+ | `bundle-url` | No | — | URL to the widget IIFE bundle — triggers [bundle mode](#bundle-mode) (non-iframe) |
245
+ | `domain-key` | Bundle mode | — | Environment key: `SGTR`, `STG`, or `PROD`. Required for bundle mode on external sites |
246
+ | `theme` | No | `light` | `light` or `dark` |
247
+ | `data-email` | Recommended | — | User's email for identification |
248
+ | `embed-token` | Access Key only | — | Short-lived token from your server |
249
+ | `width` | No | `100%` | CSS width |
250
+ | `height` | No | `600px` | CSS height |
251
+ | `locale` | No | — | BCP 47 locale tag (e.g. `fr-FR`) |
252
+ | `eager` | No | — | Load immediately instead of waiting until visible |
253
+ | `auto-refresh` | No | `true` | Auto-refresh access token before expiry |
254
+ | `debug` | No | `false` | Verbose console logging |
255
+ | `custom-data` | No | — | JSON object forwarded to the widget |
256
+
257
+ > **`widget-id` vs `community-id`:** The script-tag method uses `data-widget-id` which is a pre-encoded identifier (base64). The custom element method uses raw ObjectIds — `community-id` for most widget types, plus `channel-id` for channel widgets. The SDK handles the encoding internally.
258
+
259
+ ### Container `<div>` (script-tag method only)
260
+
261
+ The SDK looks for a `<div>` with `id="returning-ai-widget-{YOUR_WIDGET_ID}"`. Size the div to control the widget dimensions:
262
+
263
+ ```html
264
+ <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
265
+ ```
266
+
267
+ Custom elements don't need a container div — they size themselves using `width` and `height` attributes (defaults: `100%` and `600px`).
268
+
269
+ ### User Identifier Attributes
270
+
271
+ Pass any `data-*` attribute to identify the current user. Which identifiers are available is configured per community in your dashboard.
272
+
273
+ ```html
274
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
275
+ data-widget-id="YOUR_WIDGET_ID"
276
+ data-widget-type="store"
277
+ data-email="user@example.com"
278
+ data-user-id="12345"
279
+ data-account-id="abc"
280
+ api-url="YOUR_API_URL"
281
+ widget-url="YOUR_WIDGET_URL"
282
+ ></script>
283
+ ```
284
+
285
+ Any `data-*` attribute not reserved by the SDK is forwarded to the auth API as a user identifier.
286
+
287
+ ---
288
+
289
+ ## Bundle Mode
290
+
291
+ When you set the `bundle-url` attribute, the widget renders directly in the page DOM instead of inside an iframe. This gives full CSS cascade — `html[data-theme]` and Tailwind classes apply to the widget content naturally.
292
+
293
+ `widget-url` is not needed in bundle mode. `domain-key` is required so the widget resolves the correct environment URLs.
294
+
295
+ ```html
296
+ <!-- Script-tag approach -->
297
+ <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:100vh"></div>
298
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
299
+ data-widget-id="YOUR_WIDGET_ID"
300
+ data-widget-type="store"
301
+ data-theme="dark"
302
+ data-email="user@example.com"
303
+ data-bundle-url="YOUR_BUNDLE_URL"
304
+ data-domain-key="YOUR_DOMAIN_KEY"
305
+ api-url="YOUR_API_URL"
306
+ data-eager
307
+ ></script>
308
+
309
+ <!-- Custom element approach -->
310
+ <rai-store-widget
311
+ community-id="YOUR_COMMUNITY_ID"
312
+ api-url="YOUR_API_URL"
313
+ bundle-url="YOUR_BUNDLE_URL"
314
+ domain-key="YOUR_DOMAIN_KEY"
315
+ data-email="user@example.com"
316
+ theme="dark"
317
+ width="100%"
318
+ height="100vh"
319
+ eager
320
+ ></rai-store-widget>
321
+ ```
322
+
323
+ Each widget type has its own IIFE bundle and global name:
324
+
325
+ | Widget type | Bundle global | Custom element tag |
326
+ |-------------|--------------|-------------------|
327
+ | `store` | `RaiStoreWidget` | `<rai-store-widget>` |
328
+ | `channel` | `RaiChannelWidget` | `<rai-channel-widget>` |
329
+ | `social` | `RaiSocialWidget` | `<rai-social-widget>` |
330
+ | `milestone` | `RaiMilestoneWidget` | `<rai-milestone-widget>` |
331
+ | `currency-view` | `RaiCurrencyWidget` | `<rai-currency-widget>` |
332
+ | `referral-conditions` | `RaiReferralWidget` | `<rai-referral-widget>` |
333
+ | `custom` | `RaiCustomWidget` | `<rai-custom-widget>` |
334
+
335
+ The bundle must export a `mount(container, config)` function on its global (e.g. `window.RaiStoreWidget.mount`).
336
+
337
+ ---
338
+
339
+ ## Framework Usage (Custom Elements)
340
+
341
+ When using a frontend framework (React, Vue, Angular), you can use custom HTML element tags instead of the script-tag method. Custom elements use raw IDs (`community-id`, `channel-id`) instead of the pre-encoded `data-widget-id`.
342
+
343
+ ### Plain HTML
344
+
345
+ ```html
346
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"></script>
347
+
348
+ <!-- Store widget -->
349
+ <rai-store-widget
350
+ community-id="YOUR_COMMUNITY_ID"
351
+ theme="dark"
352
+ data-email="user@example.com"
353
+ api-url="YOUR_API_URL"
354
+ widget-url="YOUR_WIDGET_URL"
355
+ ></rai-store-widget>
356
+
357
+ <!-- Channel widget — requires both community-id AND channel-id -->
358
+ <rai-channel-widget
359
+ community-id="YOUR_COMMUNITY_ID"
360
+ channel-id="YOUR_CHANNEL_ID"
361
+ theme="dark"
362
+ data-email="user@example.com"
363
+ api-url="YOUR_API_URL"
364
+ widget-url="YOUR_WIDGET_URL"
365
+ ></rai-channel-widget>
366
+ ```
367
+
368
+ ### React (TypeScript)
369
+
370
+ Types are included — no manual JSX declarations needed.
371
+
372
+ ```tsx
373
+ import '@returningai/widget-sdk'
374
+
375
+ export function StoreEmbed({ email }: { email: string }) {
376
+ return (
377
+ <rai-store-widget
378
+ community-id="YOUR_COMMUNITY_ID"
379
+ theme="dark"
380
+ data-email={email}
381
+ api-url="YOUR_API_URL"
382
+ widget-url="YOUR_WIDGET_URL"
383
+ />
384
+ )
385
+ }
386
+
387
+ export function ChannelEmbed({ email }: { email: string }) {
388
+ return (
389
+ <rai-channel-widget
390
+ community-id="YOUR_COMMUNITY_ID"
391
+ channel-id="YOUR_CHANNEL_ID"
392
+ theme="dark"
393
+ data-email={email}
394
+ api-url="YOUR_API_URL"
395
+ widget-url="YOUR_WIDGET_URL"
396
+ />
397
+ )
398
+ }
399
+ ```
400
+
401
+ ### Vue 3
402
+
403
+ ```vue
404
+ <script setup>
405
+ import '@returningai/widget-sdk'
406
+ </script>
407
+
408
+ <template>
409
+ <!-- Store widget -->
410
+ <rai-store-widget
411
+ community-id="YOUR_COMMUNITY_ID"
412
+ theme="dark"
413
+ :data-email="user.email"
414
+ api-url="YOUR_API_URL"
415
+ widget-url="YOUR_WIDGET_URL"
416
+ />
417
+
418
+ <!-- Channel widget -->
419
+ <rai-channel-widget
420
+ community-id="YOUR_COMMUNITY_ID"
421
+ channel-id="YOUR_CHANNEL_ID"
422
+ theme="dark"
423
+ :data-email="user.email"
424
+ api-url="YOUR_API_URL"
425
+ widget-url="YOUR_WIDGET_URL"
426
+ />
427
+ </template>
428
+ ```
429
+
430
+ ### Angular
431
+
432
+ ```ts
433
+ // app.module.ts
434
+ import '@returningai/widget-sdk'
435
+ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'
436
+
437
+ @NgModule({ schemas: [CUSTOM_ELEMENTS_SCHEMA] })
438
+ ```
439
+
440
+ ```html
441
+ <!-- Store widget -->
442
+ <rai-store-widget
443
+ community-id="YOUR_COMMUNITY_ID"
444
+ theme="dark"
445
+ [attr.data-email]="user.email"
446
+ api-url="YOUR_API_URL"
447
+ widget-url="YOUR_WIDGET_URL"
448
+ ></rai-store-widget>
449
+
450
+ <!-- Channel widget -->
451
+ <rai-channel-widget
452
+ community-id="YOUR_COMMUNITY_ID"
453
+ channel-id="YOUR_CHANNEL_ID"
454
+ theme="dark"
455
+ [attr.data-email]="user.email"
456
+ api-url="YOUR_API_URL"
457
+ widget-url="YOUR_WIDGET_URL"
458
+ ></rai-channel-widget>
459
+ ```
460
+
461
+ ### Available custom element tags
462
+
463
+ | Tag | Required attributes | Widget type |
464
+ |-----|-------------------|-------------|
465
+ | `<rai-store-widget>` | `community-id` | Store |
466
+ | `<rai-channel-widget>` | `community-id` + `channel-id` | Channel |
467
+ | `<rai-milestone-widget>` | `community-id` | Milestone |
468
+ | `<rai-social-widget>` | `community-id` | Social |
469
+ | `<rai-currency-widget>` | `community-id` | Currency |
470
+ | `<rai-referral-widget>` | `community-id` | Referral |
471
+ | `<rai-custom-widget>` | `widget-id` (not `community-id`) | Custom |
472
+
473
+ ---
474
+
475
+ ## DOM Events
476
+
477
+ Listen for widget lifecycle events:
478
+
479
+ | Event | `detail` | Fired when |
480
+ |-------|----------|-----------|
481
+ | `rai-authenticated` | `{}` | Auth succeeded, before widget mounts |
482
+ | `rai-ready` | `{}` | `WIDGET_READY` received, loader hidden (iframe mode) |
483
+ | `rai-mounted` | `{}` | Widget bundle mounted successfully (bundle mode) |
484
+ | `rai-error` | `{ message }` | Auth failed after all retries |
485
+ | `rai-logout` | `{}` | Widget logged out |
486
+ | `rai-height-change` | `{ height }` | iframe resized (after debounce) |
487
+
488
+ ```js
489
+ // Script-tag embed
490
+ const widget = document.querySelector('[data-widget-id]')
491
+
492
+ // Custom element embed
493
+ const widget = document.querySelector('rai-store-widget')
494
+
495
+ widget.addEventListener('rai-authenticated', () => { /* auth succeeded */ })
496
+ widget.addEventListener('rai-ready', () => { /* widget loaded and visible (iframe mode) */ })
497
+ widget.addEventListener('rai-mounted', () => { /* widget mounted (bundle mode) */ })
498
+ widget.addEventListener('rai-error', (e) => console.error(e.detail.message))
499
+ widget.addEventListener('rai-logout', () => { /* user logged out */ })
500
+ widget.addEventListener('rai-height-change', (e) => console.log(e.detail.height))
501
+ ```
502
+
503
+ ---
504
+
505
+ ## Public API
506
+
507
+ After the SDK loads, `window.ReturningAIWidget` is available:
508
+
509
+ ```js
510
+ window.ReturningAIWidget.version // current SDK version
511
+ await window.ReturningAIWidget.reload() // re-runs auth flow and reloads widget
512
+ await window.ReturningAIWidget.logout() // clears tokens and removes widget
513
+ window.ReturningAIWidget.isAuthenticated() // boolean
514
+ window.ReturningAIWidget.getTokenInfo() // token metadata (no token values exposed)
515
+ ```
516
+
517
+ ---
518
+
519
+ ## Browser Support
520
+
521
+ Requires Custom Elements v1 + Shadow DOM v1.
522
+
523
+ | Browser | Minimum version |
524
+ |---------|----------------|
525
+ | Chrome | 67 |
526
+ | Firefox | 63 |
527
+ | Safari | 13 |