> ## Documentation Index
> Fetch the complete documentation index at: https://kernel.sh/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Replays

> Record and view browser sessions as mp4 videos

Replays capture browser sessions as video recordings that you can view or download later. You have full control over when replays start and stop, allowing you to capture specific interactions or workflows.

## Starting and stopping recordings

To start recording a browser session, use the replays API on an active browser:

<CodeGroup>
  ```typescript Typescript/Javascript theme={null}
  import Kernel from '@onkernel/sdk';

  const kernel = new Kernel();
  const kernelBrowser = await kernel.browsers.create();

  const replay = await kernel.browsers.replays.start(kernelBrowser.session_id);
  console.log(`Recording started with ID: ${replay.replay_id}`);

  // Perform some automation...

  await kernel.browsers.replays.stop(replay.replay_id, { id: kernelBrowser.session_id });
  console.log('Recording stopped and processing');
  ```

  ```python Python theme={null}
  from kernel import Kernel

  kernel = Kernel()
  kernel_browser = kernel.browsers.create()

  replay = kernel.browsers.replays.start(kernel_browser.session_id)
  print(f"Recording started with ID: {replay.replay_id}")

  # Perform some automation...

  kernel.browsers.replays.stop(replay_id=replay.replay_id, id=kernel_browser.session_id)
  print("Recording stopped and processing")
  ```

  ```go Go theme={null}
  package main

  import (
  	"context"
  	"fmt"

  	"github.com/kernel/kernel-go-sdk"
  )

  func main() {
  	ctx := context.Background()
  	client := kernel.NewClient()

  	kernelBrowser, err := client.Browsers.New(ctx, kernel.BrowserNewParams{})
  	if err != nil {
  		panic(err)
  	}

  	replay, err := client.Browsers.Replays.Start(ctx, kernelBrowser.SessionID, kernel.BrowserReplayStartParams{})
  	if err != nil {
  		panic(err)
  	}
  	fmt.Printf("Recording started with ID: %s\n", replay.ReplayID)

  	// Perform some automation...

  	if err := client.Browsers.Replays.Stop(ctx, replay.ReplayID, kernel.BrowserReplayStopParams{
  		ID: kernelBrowser.SessionID,
  	}); err != nil {
  		panic(err)
  	}
  	fmt.Println("Recording stopped and processing")
  }
  ```
</CodeGroup>

## Multiple recordings per session

You can create multiple replay recordings for a single browser session. Each recording gets a unique `replay_id` and can be started and stopped independently:

<CodeGroup>
  ```typescript Typescript/Javascript theme={null}
  const replay1 = await kernel.browsers.replays.start(kernelBrowser.session_id);
  // Perform some automation...
  await kernel.browsers.replays.stop(replay1.replay_id, { id: kernelBrowser.session_id });

  const replay2 = await kernel.browsers.replays.start(kernelBrowser.session_id);
  // Perform different automation...
  await kernel.browsers.replays.stop(replay2.replay_id, { id: kernelBrowser.session_id });
  ```

  ```python Python theme={null}
  replay1 = kernel.browsers.replays.start(kernel_browser.session_id)
  # Perform some automation...
  kernel.browsers.replays.stop(replay_id=replay1.replay_id, id=kernel_browser.session_id)

  replay2 = kernel.browsers.replays.start(kernel_browser.session_id)
  # Perform different automation...
  kernel.browsers.replays.stop(replay_id=replay2.replay_id, id=kernel_browser.session_id)
  ```

  ```go Go theme={null}
  replay1, err := client.Browsers.Replays.Start(ctx, kernelBrowser.SessionID, kernel.BrowserReplayStartParams{})
  if err != nil {
  	panic(err)
  }
  // Perform some automation...
  if err := client.Browsers.Replays.Stop(ctx, replay1.ReplayID, kernel.BrowserReplayStopParams{
  	ID: kernelBrowser.SessionID,
  }); err != nil {
  	panic(err)
  }

  replay2, err := client.Browsers.Replays.Start(ctx, kernelBrowser.SessionID, kernel.BrowserReplayStartParams{})
  if err != nil {
  	panic(err)
  }
  // Perform different automation...
  if err := client.Browsers.Replays.Stop(ctx, replay2.ReplayID, kernel.BrowserReplayStopParams{
  	ID: kernelBrowser.SessionID,
  }); err != nil {
  	panic(err)
  }
  ```
</CodeGroup>

## Downloading all replays

To access all replays for a browser session, list and access them via url or as downloads:

<CodeGroup>
  ```typescript Typescript/Javascript theme={null}
  import fs from 'fs';
  import { Buffer } from 'buffer';

  const replays = await kernel.browsers.replays.list(kernelBrowser.session_id);

  for (const replay of replays) {
    console.log(`Replay ID: ${replay.replay_id}`);
    console.log(`View URL: ${replay.replay_view_url}`);

    const videoData = await kernel.browsers.replays.download(
      replay.replay_id,
      { id: kernelBrowser.session_id }
    );

    const content = await videoData.blob();
    const buffer = Buffer.from(await content.arrayBuffer());

    const filename = `replay-${replay.replay_id}-${kernelBrowser.session_id}.mp4`;
    fs.writeFileSync(filename, buffer);
  }
  ```

  ```python Python theme={null}
  import aiofiles

  replays = kernel.browsers.replays.list(kernel_browser.session_id)

  for replay in replays:
      print(f"Replay ID: {replay.replay_id}")
      print(f"View URL: {replay.replay_view_url}")

      video_data = kernel.browsers.replays.download(
          replay_id=replay.replay_id,
          id=kernel_browser.session_id
      )

      content = video_data.read()

      filename = f"replay-{replay.replay_id}-{kernel_browser.session_id}.mp4"
      async with aiofiles.open(filename, 'wb') as f:
          await f.write(content)

      print(f"Saved replay to {filename}")
  ```

  ```go Go theme={null}
  package main

  import (
  	"context"
  	"fmt"
  	"io"
  	"os"

  	"github.com/kernel/kernel-go-sdk"
  )

  func main() {
  	ctx := context.Background()
  	client := kernel.NewClient()

  	sessionID := "brw_01jwv4tn5m8k3q2v7x9p0a1bc2"

  	replays, err := client.Browsers.Replays.List(ctx, sessionID)
  	if err != nil {
  		panic(err)
  	}

  	for _, replay := range *replays {
  		fmt.Printf("Replay ID: %s\n", replay.ReplayID)
  		fmt.Printf("View URL: %s\n", replay.ReplayViewURL)

  		videoData, err := client.Browsers.Replays.Download(ctx, replay.ReplayID, kernel.BrowserReplayDownloadParams{
  			ID: sessionID,
  		})
  		if err != nil {
  			panic(err)
  		}

  		filename := fmt.Sprintf("replay-%s-%s.mp4", replay.ReplayID, sessionID)
  		file, err := os.Create(filename)
  		if err != nil {
  			videoData.Body.Close()
  			panic(err)
  		}

  		_, copyErr := io.Copy(file, videoData.Body)
  		closeErr := videoData.Body.Close()
  		fileErr := file.Close()
  		if copyErr != nil {
  			panic(copyErr)
  		}
  		if closeErr != nil {
  			panic(closeErr)
  		}
  		if fileErr != nil {
  			panic(fileErr)
  		}

  		fmt.Printf("Saved replay to %s\n", filename)
  	}
  }
  ```
</CodeGroup>

## Embedding a replay

Set an iframe's `src` to the `replay_view_url` returned when you start or list a replay. Give the iframe an explicit aspect ratio so the player keeps its shape while loading, and enable fullscreen playback. Merge the example's `frame-src` sources into your existing Content Security Policy.

Replace `REPLAY_VIEW_URL` with the URL returned by the API:

```html theme={null}
<meta
  http-equiv="Content-Security-Policy"
  content="frame-src https://*.kernel.sh:8443 https://*.onkernel.com:8443 https://kernel-api-prod.s3.us-east-1.amazonaws.com;"
>

<div style="width: 100%; aspect-ratio: 16 / 10;">
  <iframe
    src="REPLAY_VIEW_URL"
    title="Browser session replay"
    loading="lazy"
    referrerpolicy="no-referrer"
    allow="fullscreen"
    allowfullscreen
    style="display: block; width: 100%; height: 100%; border: 0;"
  ></iframe>
</div>
```

Use the recorded browser's viewport ratio if it differs from `16 / 10`. Treat replay URLs as sensitive.
