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.json files.
  • 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.

JavaScript
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.');
TypeScript
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

TypeScript
interface RecordingState {
  mode: 'idle' | 'recording' | 'saving' | 'playing';
  elapsedMs: number;   // recorded or played so far
  error?: string;      // the last error shown to the user
}
MethodBehavior
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.

TypeScript
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';
}
MethodBehavior
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.

MethodBehavior
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

JavaScript
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.

TypeScript
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
}
MethodBehavior
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

JavaScript
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

AttributeEffect
data-weei-ignoreThe element and its contents are not recorded at all.
data-weei-no-replayActions on the element are recorded but skipped during playback, for example a Pay button.
HTML
<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.