Widget ID: recording-demos. API: documented below.
Purpose
Recording Demos has two jobs:
- Recordings. Record a walkthrough of your application — clicks, input, scrolling, plus camera and microphone if enabled — and replay it in the live application, alone or together with other participants (Watch together). Recordings are saved to your site's library on Weei servers and can be downloaded as
.weei-demo.jsonfiles. - Issues. Weei keeps the last moments of user activity in the browser. When an error happens, or a user reports a problem, the issue is captured together with that activity and your application's own events, so you can replay what led to it.
Playback repeats actions in your application; it does not record the screen. Open the same page in the same starting state before replaying.
Getting the API
The API is available as soon as loadWeei() resolves, if the widget is enabled for your site. The widget window does not need to be open.
const weei = await window.weeiReady;
const recordings = weei.widgetsApi.get('recording-demos');
if (!recordings) throw new Error('Recording Demos is not enabled for this site.');interface RecordingDemosApi {
// Recording
getRecordingState(): RecordingState;
startRecording(): void;
stopRecording(details?: { name?: string; description?: string }): Promise<DemoSummary | undefined>;
// Library
listDemos(): DemoSummary[];
refreshDemos(): Promise<DemoSummary[]>;
deleteDemo(demoId: string): Promise<void>;
exportDemo(demoId: string): Promise<File>; // a .weei-demo.json file
importDemo(file: File): Promise<DemoSummary>; // and saves it to the library
// Playback
play(demoId: string): Promise<void>;
playTogether(demoId: string): Promise<void>; // every connected participant, in sync
stopPlayback(): void;
// Changes
subscribe(listener: (state: RecordingState, demos: DemoSummary[]) => void): () => void;
// Event journal
reportEvent(event: DiagnosticEventInput): void;
getEvents(): DiagnosticEvent[];
clear(): void;
}Recording
interface RecordingState {
mode: 'idle' | 'recording' | 'saving' | 'playing';
elapsedMs: number; // recorded or played so far
error?: string; // the last error shown to the user
}| Method | Behavior |
|---|---|
startRecording() | Starts recording this page. Does nothing unless the mode is idle. Throws if the widget is not available to this user. |
stopRecording(details?) | Stops, then uploads the recording to your site's library and resolves with its summary. name is up to 120 characters (default Recorded demo), description up to 10,000. Resolves with undefined when nothing was being recorded; rejects if the recording exceeds 50 MB. |
Recording captures clicks, input, keyboard and scrolling in your page, plus the camera and microphone if a participant turns them on. It does not record the screen.
Library
Recordings are stored per site: everyone on the same origin (scheme, host and port) sees the same library. The API returns summaries; actions and media stay inside Weei.
interface DemoSummary {
id: string;
name: string;
description: string;
kind: 'demo' | 'issue'; // issue: captured together with a reported problem
createdAt: number; // ms since epoch
url: string; // the page it started on
width: number; // the window size it was recorded in, px
height: number;
durationMs: number;
actionCount: number;
hasAudio: boolean;
hasVideo: boolean;
saveState: 'saved' | 'saving' | 'error';
}| Method | Behavior |
|---|---|
listDemos() | The recordings loaded in this browser, newest first. |
refreshDemos() | Reloads the library from the server, including changes made by other users. |
deleteDemo(id) | Removes a recording from your site's library for everyone. Downloaded files are not affected. |
exportDemo(id) | Returns the recording as a .weei-demo.json file, up to 50 MB. |
importDemo(file) | Reads a .weei-demo.json file and saves it to the library. |
Playback
Playback repeats the recorded actions in the live page; it does not show a video of the screen. Start it on the same origin, from the same starting state; a recording of another site is rejected. If an element is missing, playback stops with an error in RecordingState.error.
| Method | Behavior |
|---|---|
play(id) | Replays the recording for this participant. Does nothing unless the mode is idle. |
playTogether(id) | Sends the recording to connected participants and starts it for everyone at the same moment, about one second later. |
stopPlayback() | Stops playback; after playTogether(), stops it for everyone. |
Example: record a walkthrough from your own button
const weei = await window.weeiReady;
const recordings = weei.widgetsApi.get('recording-demos');
const button = document.querySelector('#record');
recordings.subscribe(state => {
button.textContent = state.mode === 'recording'
? 'Stop (' + Math.round(state.elapsedMs / 1000) + ' s)'
: 'Record a walkthrough';
button.disabled = state.mode === 'saving' || state.mode === 'playing';
});
button.onclick = async () => {
if (recordings.getRecordingState().mode === 'recording') {
const saved = await recordings.stopRecording({ name: 'Onboarding walkthrough' });
console.log('Saved', saved?.id);
} else {
recordings.startRecording();
}
};Event journal
Reports your application's own events, for example a failed payment. They are attached to captured issues and recordings and shared with the session's participants.
interface DiagnosticEventInput {
message: string; // up to 2,000 characters
level?: 'info' | 'warning' | 'error'; // default 'info'
source?: string; // up to 120 characters
occurredAt?: number; // milliseconds; default now
context?: { url?: string; stack?: string };
}
interface DiagnosticEvent extends DiagnosticEventInput {
id: string;
level: 'info' | 'warning' | 'error';
time: number;
user: string; // peer ID of the participant who reported it
}| Method | Behavior |
|---|---|
reportEvent(event) | Adds an event to the session's diagnostic log and attaches it to captured issues. A blank message is ignored; an unknown level throws. |
getEvents() | A copy of the current log, including events from other participants. |
clear() | Clears this browser's log. Saved issues and recordings are not affected. |
Example: report a failed payment
async function reportPaymentFailure(error) {
const weei = await window.weeiReady;
weei.widgetsApi.get('recording-demos')?.reportEvent({
message: 'Payment confirmation failed',
level: 'error',
source: 'checkout',
context: { url: location.origin + location.pathname, stack: error?.stack }
});
}Protect sensitive actions
| Attribute | Effect |
|---|---|
data-weei-ignore | The element and its contents are not recorded at all. |
data-weei-no-replay | Actions on the element are recorded but skipped during playback, for example a Pay button. |
<button data-weei-no-replay>Pay</button>
<form data-weei-ignore><!-- card details --></form>In captured issues, values typed into fields are replaced with synthetic text of the same shape before the issue is saved. Password fields are never captured.