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

# WebSocket API

> Complete reference for the WebSocket message protocol between frontend and backend

## Connection

The WebSocket server runs on `ws://localhost:44030/` and accepts connections from the frontend.

```typescript theme={null}
const socket = new WebSocket('ws://localhost:44030/');
```

<Note>
  The backend only allows one active WebSocket connection. New connections automatically close previous ones.
</Note>

<Note>
  The backend also listens on the legacy port `ws://localhost:5000/`. Connections to that port immediately receive an `AppVersion` message and are then closed, which causes older frontends to detect a version mismatch and reload against the current port.
</Note>

## Message Structure

### Frontend to Backend

Messages sent via `window.external.sendMessage()` use this JSON structure:

```json theme={null}
{
  "Method": "string",
  "Parameters": {
    // Method-specific parameters
  }
}
```

### Backend to Frontend

Messages received via WebSocket use this JSON structure:

```json theme={null}
{
  "method": "string",
  "content": {
    // Event-specific data
  }
}
```

<Warning>
  Note the casing difference: `Method` (capitalized) for frontend-to-backend, `method` (lowercase) for backend-to-frontend due to JSON serialization settings.
</Warning>

## Frontend to Backend Methods

These commands can be sent from the frontend to control the backend.

### Recording Control

<ParamField path="StartRecording" type="method">
  Starts a manual recording session.

  **Parameters:** None

  **Example:**

  ```typescript theme={null}
  sendMessageToBackend('StartRecording');
  ```
</ParamField>

<ParamField path="StopRecording" type="method">
  Stops the current recording.

  **Parameters:** None

  **Example:**

  ```typescript theme={null}
  sendMessageToBackend('StopRecording');
  ```
</ParamField>

### Authentication

<ParamField path="Login" type="method">
  Authenticates the user with Supabase tokens.

  **Parameters:**

  <ResponseField name="accessToken" type="string" required>
    Supabase access token from the session
  </ResponseField>

  <ResponseField name="refreshToken" type="string" required>
    Supabase refresh token for token renewal
  </ResponseField>

  **Example:**

  ```typescript theme={null}
  sendMessageToBackend('Login', {
    accessToken: session.access_token,
    refreshToken: session.refresh_token,
  });
  ```
</ParamField>

<ParamField path="Logout" type="method">
  Signs out the current user.

  **Parameters:** None

  **Example:**

  ```typescript theme={null}
  sendMessageToBackend('Logout');
  ```
</ParamField>

### Clip Creation

<ParamField path="CreateClip" type="method">
  Creates clips from selected video segments.

  **Parameters:**

  <ResponseField name="Segments" type="array" required>
    Array of segment objects defining clip ranges

    <ResponseField name="id" type="number" required>
      Unique identifier for this segment
    </ResponseField>

    <ResponseField name="startTime" type="number" required>
      Start timestamp in seconds
    </ResponseField>

    <ResponseField name="endTime" type="number" required>
      End timestamp in seconds
    </ResponseField>

    <ResponseField name="fileName" type="string" required>
      Source video file name
    </ResponseField>

    <ResponseField name="filePath" type="string">
      Full path to source video
    </ResponseField>

    <ResponseField name="type" type="string" required>
      Content type: `Session`, `Buffer`, `Clip`, or `Highlight`
    </ResponseField>

    <ResponseField name="game" type="string" required>
      Game name for organizing clips
    </ResponseField>

    <ResponseField name="title" type="string">
      Optional title for the clip
    </ResponseField>

    <ResponseField name="igdbId" type="number">
      IGDB game database ID
    </ResponseField>

    <ResponseField name="mutedAudioTracks" type="number[]">
      Optional array of audio track indexes to mute in the output
    </ResponseField>

    <ResponseField name="audioTrackVolumes" type="object">
      Optional map of `{ "<trackIndex>": <volume> }` to apply per-track volume adjustments
    </ResponseField>
  </ResponseField>

  **Example:**

  ```typescript theme={null}
  sendMessageToBackend('CreateClip', {
    Segments: [{
      id: 1,
      startTime: 30.5,
      endTime: 45.2,
      fileName: "gameplay_2024",
      filePath: "C:/Videos/gameplay_2024.mp4",
      type: "Session",
      game: "Valorant",
      title: "Epic Ace",
      igdbId: 12345
    }]
  });
  ```
</ParamField>

<ParamField path="CancelClip" type="method">
  Cancels an in-progress clip operation.

  **Parameters:**

  <ResponseField name="id" type="number" required>
    The clip operation ID to cancel
  </ResponseField>

  **Example:**

  ```typescript theme={null}
  sendMessageToBackend('CancelClip', { id: 123 });
  ```
</ParamField>

<ParamField path="CreateAiClip" type="method">
  Creates AI-generated highlights from a video.

  **Parameters:**

  <ResponseField name="FileName" type="string" required>
    Video file name to process
  </ResponseField>

  **Example:**

  ```typescript theme={null}
  sendMessageToBackend('CreateAiClip', {
    FileName: "gameplay_2024.mp4"
  });
  ```
</ParamField>

### Content Management

<ParamField path="DeleteContent" type="method">
  Deletes a single piece of content.

  **Parameters:**

  <ResponseField name="FileName" type="string" required>
    File name of the content to delete
  </ResponseField>

  <ResponseField name="ContentType" type="string" required>
    Type of content: `Session`, `Buffer`, `Clip`, or `Highlight`
  </ResponseField>

  **Example:**

  ```typescript theme={null}
  sendMessageToBackend('DeleteContent', {
    FileName: "clip_001",
    ContentType: "Clip"
  });
  ```
</ParamField>

<ParamField path="DeleteMultipleContent" type="method">
  Deletes multiple content items in a batch operation.

  **Parameters:**

  <ResponseField name="Items" type="array" required>
    Array of items to delete, each with FileName and ContentType
  </ResponseField>

  **Example:**

  ```typescript theme={null}
  sendMessageToBackend('DeleteMultipleContent', {
    Items: [
      { FileName: "clip_001.mp4", ContentType: "Clip" },
      { FileName: "clip_002.mp4", ContentType: "Clip" }
    ]
  });
  ```
</ParamField>

<ParamField path="RenameContent" type="method">
  Renames a content file.

  **Parameters:** Handled by ContentService.HandleRenameContent

  **Example:**

  ```typescript theme={null}
  sendMessageToBackend('RenameContent', {
    // Parameters defined in ContentService
  });
  ```
</ParamField>

<ParamField path="ImportFile" type="method">
  Imports an external video file into Segra.

  **Parameters:** Handled by ImportService.HandleImportFile

  **Example:**

  ```typescript theme={null}
  sendMessageToBackend('ImportFile', {
    // Parameters defined in ImportService
  });
  ```
</ParamField>

### Upload & Compression

<ParamField path="UploadContent" type="method">
  Uploads content to a platform (e.g., Segra.tv).

  **Parameters:** Handled by UploadService.HandleUploadContent

  **Example:**

  ```typescript theme={null}
  sendMessageToBackend('UploadContent', {
    // Parameters defined in UploadService
  });
  ```
</ParamField>

<ParamField path="CancelUpload" type="method">
  Cancels an in-progress upload.

  **Parameters:**

  <ResponseField name="fileName" type="string" required>
    File name of the upload to cancel
  </ResponseField>

  **Example:**

  ```typescript theme={null}
  sendMessageToBackend('CancelUpload', {
    fileName: "clip_001.mp4"
  });
  ```
</ParamField>

<ParamField path="CompressVideo" type="method">
  Compresses a video to reduce file size.

  **Parameters:**

  <ResponseField name="FilePath" type="string" required>
    Full path to the video file
  </ResponseField>

  **Example:**

  ```typescript theme={null}
  sendMessageToBackend('CompressVideo', {
    FilePath: "C:/Videos/large_file.mp4"
  });
  ```
</ParamField>

### Bookmarks

<ParamField path="AddBookmark" type="method">
  Adds a bookmark during recording or playback.

  **Parameters:** Handled by ContentService.HandleAddBookmark

  **Example:**

  ```typescript theme={null}
  sendMessageToBackend('AddBookmark', {
    // Parameters defined in ContentService
  });
  ```
</ParamField>

<ParamField path="DeleteBookmark" type="method">
  Removes a bookmark.

  **Parameters:** Handled by ContentService.HandleDeleteBookmark

  **Example:**

  ```typescript theme={null}
  sendMessageToBackend('DeleteBookmark', {
    // Parameters defined in ContentService
  });
  ```
</ParamField>

### Settings

<ParamField path="UpdateSettings" type="method">
  Updates application settings.

  **Parameters:** Handled by SettingsService.HandleUpdateSettings

  **Example:**

  ```typescript theme={null}
  sendMessageToBackend('UpdateSettings', {
    // Settings object with changed values
  });
  ```
</ParamField>

<ParamField path="SetVideoLocation" type="method">
  Opens a folder picker to change the video storage location.

  **Parameters:** None

  **Example:**

  ```typescript theme={null}
  sendMessageToBackend('SetVideoLocation');
  ```
</ParamField>

<ParamField path="SetCacheLocation" type="method">
  Opens a folder picker to change the cache storage location.

  **Parameters:** None

  **Example:**

  ```typescript theme={null}
  sendMessageToBackend('SetCacheLocation');
  ```
</ParamField>

<ParamField path="ApplyVideoPreset" type="method">
  Applies a video quality preset (see `PresetsService.ApplyVideoPreset`).

  **Parameters:**

  <ResponseField name="preset" type="string" required>
    Preset name: `low`, `standard`, `high`, or `custom`
  </ResponseField>

  **Example:**

  ```typescript theme={null}
  sendMessageToBackend('ApplyVideoPreset', {
    preset: "high"
  });
  ```
</ParamField>

<ParamField path="ApplyClipPreset" type="method">
  Applies a clip quality preset (see `PresetsService.ApplyClipPreset`).

  **Parameters:**

  <ResponseField name="preset" type="string" required>
    Preset name: `low`, `standard`, `high`, or `custom`
  </ResponseField>

  **Example:**

  ```typescript theme={null}
  sendMessageToBackend('ApplyClipPreset', {
    preset: "standard"
  });
  ```
</ParamField>

### Game Management

<Note>
  Per-game record/skip preferences live on the unified `games` array in `Settings.Instance` (each entry has a `record` boolean plus optional quality / recording-mode overrides). Update them by sending an `UpdateSettings` message with the modified `games` array — there are no dedicated `AddToWhitelist` / `AddToBlacklist` / `MoveGame` methods. The legacy `whitelist` / `blacklist` fields are migration-only and may be removed in a future release.
</Note>

<ParamField path="SelectGameExecutable" type="method">
  Opens a file picker to select a game executable.

  **Parameters:** None

  **Response:** Backend sends `SelectedGameExecutable` message with the selected file

  **Example:**

  ```typescript theme={null}
  sendMessageToBackend('SelectGameExecutable');
  ```
</ParamField>

### System Actions

<ParamField path="OpenFileLocation" type="method">
  Opens Windows Explorer to the file location.

  **Parameters:**

  <ResponseField name="FilePath" type="string" required>
    Full path to the file
  </ResponseField>

  **Example:**

  ```typescript theme={null}
  sendMessageToBackend('OpenFileLocation', {
    FilePath: "C:/Videos/clip.mp4"
  });
  ```
</ParamField>

<ParamField path="CopyFileToClipboard" type="method">
  Copies a file to the Windows clipboard so it can be pasted into Explorer, chat clients, or other apps that accept file drops.

  **Parameters:**

  <ResponseField name="FilePath" type="string" required>
    Full path to the file to copy
  </ResponseField>

  **Example:**

  ```typescript theme={null}
  sendMessageToBackend('CopyFileToClipboard', {
    FilePath: "C:/Videos/clip.mp4"
  });
  ```
</ParamField>

<ParamField path="OpenInBrowser" type="method">
  Opens a URL in the default browser.

  **Parameters:**

  <ResponseField name="Url" type="string" required>
    URL to open
  </ResponseField>

  **Example:**

  ```typescript theme={null}
  sendMessageToBackend('OpenInBrowser', {
    Url: "https://segra.tv"
  });
  ```
</ParamField>

<ParamField path="OpenLogsLocation" type="method">
  Opens Windows Explorer to the logs folder.

  **Parameters:** None

  **Example:**

  ```typescript theme={null}
  sendMessageToBackend('OpenLogsLocation');
  ```
</ParamField>

<ParamField path="ToggleFullscreen" type="method">
  Toggles fullscreen mode.

  **Parameters:**

  <ResponseField name="enabled" type="boolean" required>
    True to enable fullscreen, false to disable
  </ResponseField>

  **Example:**

  ```typescript theme={null}
  sendMessageToBackend('ToggleFullscreen', {
    enabled: true
  });
  ```
</ParamField>

### Updates

<ParamField path="CheckForUpdates" type="method">
  Checks for available application updates.

  **Parameters:** None

  **Example:**

  ```typescript theme={null}
  sendMessageToBackend('CheckForUpdates');
  ```
</ParamField>

<ParamField path="ApplyUpdate" type="method">
  Applies a downloaded update and restarts the application.

  **Parameters:** None

  **Example:**

  ```typescript theme={null}
  sendMessageToBackend('ApplyUpdate');
  ```
</ParamField>

### Connection

<ParamField path="NewConnection" type="method">
  Signals that a new WebSocket connection has been established. The backend responds with initial state.

  **Parameters:** None

  **Response:** Backend sends:

  * `Settings` with persisted settings
  * `State` with runtime application state
  * `GameList` with available games
  * `AppVersion` with backend version (only when an update manager build is available)
  * `UpdateProgress` if an update download/install was in flight when the client (re)connected, replaying the most recent progress payload
  * `ReleaseNotes` asynchronously, after fetching from the update channel

  **Example:**

  ```typescript theme={null}
  sendMessageToBackend('NewConnection');
  ```
</ParamField>

### Storage & Recovery

<ParamField path="StorageWarningConfirm" type="method">
  Confirms or cancels a storage warning action.

  **Parameters:** Handled by StorageWarningService.HandleStorageWarningConfirm
</ParamField>

<ParamField path="RecoveryConfirm" type="method">
  Confirms recovery of temporary files after a crash.

  **Parameters:** Handled by RecoveryService.HandleRecoveryConfirm
</ParamField>

<ParamField path="MigrateContent" type="method">
  Triggers migration of existing content into Segra's canonical folder layout.

  **Parameters:** Handled by ContentMigrationService.HandleMigrateContent

  **Progress:** The backend streams `ContentMigrationProgress` events while the migration runs.
</ParamField>

***

## Backend to Frontend Events

These messages are sent from the backend to update the frontend state.

### State Updates

<ResponseField name="Settings" type="event">
  Sends the persisted user settings to the frontend. `content` is the bare `Settings.Instance` object (no wrapping `settings` key).

  **Example:**

  ```json theme={null}
  {
    "method": "Settings",
    "content": {
      "contentFolder": "C:/Videos",
      "cacheFolder": "C:/AppData/Segra",
      "resolution": "1440p",
      "frameRate": 60,
      "recordingMode": "Hybrid",
      "keybindings": [...]
      // ... all persisted settings
    }
  }
  ```

  <Note>
    Runtime state (active recording, content library, detected devices, current folder size, etc.) is **no longer** nested under `settings.state`. It is delivered separately via the `State` message described below.
  </Note>
</ResponseField>

<ResponseField name="State" type="event">
  Sends the runtime `AppState.Instance` object to the frontend. This is non-persisted, in-memory state that mirrors the live application status.

  **Content (selected fields):**

  <ResponseField name="recording" type="object">
    The active `Recording` (or `null` when idle)
  </ResponseField>

  <ResponseField name="preRecording" type="object">
    A `PreRecording` placeholder while Segra is preparing to record (or `null`)
  </ResponseField>

  <ResponseField name="content" type="array">
    All known content items (sessions, buffers, clips, highlights)
  </ResponseField>

  <ResponseField name="inputDevices" type="array">
    Detected audio input devices
  </ResponseField>

  <ResponseField name="outputDevices" type="array">
    Detected audio output devices
  </ResponseField>

  <ResponseField name="displays" type="array">
    Detected monitors
  </ResponseField>

  <ResponseField name="codecs" type="array">
    Available video codecs/encoders
  </ResponseField>

  <ResponseField name="availableOBSVersions" type="array">
    OBS versions known to the updater
  </ResponseField>

  <ResponseField name="gpuVendor" type="string">
    Detected GPU vendor (`Nvidia`, `Amd`, `Intel`, or `Unknown`)
  </ResponseField>

  <ResponseField name="cudaComputeCapability" type="number">
    Detected CUDA compute capability for NVIDIA GPUs
  </ResponseField>

  <ResponseField name="maxDisplayHeight" type="number">
    Maximum height of any connected display (used for resolution clamping)
  </ResponseField>

  <ResponseField name="hasLoadedObs" type="boolean">
    True once OBS has been initialized
  </ResponseField>

  <ResponseField name="isCheckingForUpdates" type="boolean">
    True while an update check is in-flight
  </ResponseField>

  <ResponseField name="currentFolderSizeGb" type="number">
    Total size of the content folder in GB
  </ResponseField>

  **Example:**

  ```json theme={null}
  {
    "method": "State",
    "content": {
      "recording": null,
      "preRecording": null,
      "content": [],
      "gpuVendor": "Nvidia",
      "currentFolderSizeGb": 12.4,
      "hasLoadedObs": true
    }
  }
  ```
</ResponseField>

<ResponseField name="GameList" type="event">
  Sends the list of detected games.

  **Content:** The `content` field is itself an array of `GameEntry` objects — there is no wrapping `games` key.

  **Example:**

  ```json theme={null}
  {
    "method": "GameList",
    "content": [
      {
        "name": "Valorant",
        "executables": ["C:\\Games\\Valorant\\VALORANT.exe"]
      }
    ]
  }
  ```

  <Note>
    Each entry uses the `GameEntry` shape from `Backend/Games/GameUtils.cs` — `name` and `executables` (back-slashed). It is distinct from the `Game` shape used for whitelist/blacklist commands, which uses `paths`.
  </Note>
</ResponseField>

<ResponseField name="AppVersion" type="event">
  Sends the backend application version for version mismatch detection.

  **Content:**

  <ResponseField name="version" type="string">
    Semantic version string (e.g., "1.2.3")
  </ResponseField>

  **Example:**

  ```json theme={null}
  {
    "method": "AppVersion",
    "content": {
      "version": "1.2.3"
    }
  }
  ```

  <Note>
    Frontend automatically reloads if this doesn't match `__APP_VERSION__`
  </Note>
</ResponseField>

### Progress Events

<ResponseField name="ClipProgress" type="event">
  Reports progress of clip creation operations.

  **Content:**

  <ResponseField name="id" type="number" required>
    Clip operation ID
  </ResponseField>

  <ResponseField name="progress" type="number" required>
    Progress percentage (0-100), or -1 for error
  </ResponseField>

  <ResponseField name="segments" type="array" required>
    Array of segments being processed (mirrors what was sent in `CreateClip`)
  </ResponseField>

  <ResponseField name="error" type="string">
    Error message if progress is -1
  </ResponseField>

  **Example:**

  ```json theme={null}
  {
    "method": "ClipProgress",
    "content": {
      "id": 123456,
      "progress": 45,
      "segments": [...]
    }
  }
  ```
</ResponseField>

<ResponseField name="UploadProgress" type="event">
  Reports progress of upload operations.

  **Content:**

  <ResponseField name="fileName" type="string" required>
    File being uploaded
  </ResponseField>

  <ResponseField name="title" type="string" required>
    Title submitted with the upload (mirrored in every progress event)
  </ResponseField>

  <ResponseField name="progress" type="number" required>
    Progress percentage (0-100), or -1 for error
  </ResponseField>

  <ResponseField name="status" type="string" required>
    Upload status: "uploading", "processing", "done", or "error"
  </ResponseField>

  <ResponseField name="message" type="string">
    Status message or error description
  </ResponseField>

  **Example:**

  ```json theme={null}
  {
    "method": "UploadProgress",
    "content": {
      "fileName": "clip_001.mp4",
      "progress": 75,
      "status": "uploading",
      "message": "Uploading to server..."
    }
  }
  ```
</ResponseField>

<ResponseField name="ImportProgress" type="event">
  Reports progress of file import operations.

  **Content:**

  <ResponseField name="id" type="number" required>
    Import operation ID
  </ResponseField>

  <ResponseField name="fileName" type="string" required>
    Current file being imported
  </ResponseField>

  <ResponseField name="progress" type="number" required>
    Progress percentage for current file
  </ResponseField>

  <ResponseField name="status" type="string" required>
    Import status: "importing", "done", or "error"
  </ResponseField>

  <ResponseField name="totalFiles" type="number" required>
    Total number of files to import
  </ResponseField>

  <ResponseField name="currentFileIndex" type="number" required>
    Index of current file (0-based)
  </ResponseField>

  <ResponseField name="message" type="string">
    Status message
  </ResponseField>
</ResponseField>

<ResponseField name="CompressionProgress" type="event">
  Reports progress of video compression.

  **Content:**

  <ResponseField name="filePath" type="string" required>
    File being compressed
  </ResponseField>

  <ResponseField name="progress" type="number" required>
    Progress percentage (0-100), or -1 for error
  </ResponseField>

  <ResponseField name="status" type="string" required>
    Status: "compressing", "done", "skipped", or "error"
  </ResponseField>

  <ResponseField name="message" type="string">
    Status message
  </ResponseField>

  **Example:**

  ```json theme={null}
  {
    "method": "CompressionProgress",
    "content": {
      "filePath": "C:/Videos/large.mp4",
      "progress": 50,
      "status": "compressing"
    }
  }
  ```
</ResponseField>

<ResponseField name="UpdateProgress" type="event">
  Reports progress of application updates.

  **Content:**

  <ResponseField name="version" type="string" required>
    Version being downloaded
  </ResponseField>

  <ResponseField name="progress" type="number" required>
    Download progress (0-100)
  </ResponseField>

  <ResponseField name="status" type="string" required>
    Status: "downloading", "downloaded", or "ready"
  </ResponseField>

  <ResponseField name="message" type="string" required>
    Human-readable status message
  </ResponseField>
</ResponseField>

<ResponseField name="AiProgress" type="event">
  Reports progress of AI highlight generation.

  **Content:** Handled by AiService
</ResponseField>

<ResponseField name="RecordingPreviewState" type="event">
  Indicates whether the recording-preview window is currently active (the in-app live preview controlled by the `TogglePreview` keybind).

  **Content:**

  <ResponseField name="enabled" type="boolean" required>
    Whether preview rendering is active
  </ResponseField>
</ResponseField>

<ResponseField name="RecordingPreviewFrame" type="event">
  Streams a frame of the live recording preview to the frontend (sent only while the preview is enabled).

  **Content:** A frame payload emitted by `RecordingPreviewService`.
</ResponseField>

<ResponseField name="MigrationStatus" type="event">
  Reports status of data migration operations.

  **Content:**

  <ResponseField name="isRunning" type="boolean" required>
    Whether migration is currently running
  </ResponseField>

  <ResponseField name="currentMigration" type="string">
    Description of current migration
  </ResponseField>
</ResponseField>

<ResponseField name="ContentMigrationProgress" type="event">
  Reports progress of a content-folder migration triggered by `MigrateContent`.

  **Content:**

  <ResponseField name="id" type="string" required>
    Migration operation ID
  </ResponseField>

  <ResponseField name="fileName" type="string" required>
    File currently being moved
  </ResponseField>

  <ResponseField name="progress" type="number" required>
    Progress percentage (0-100), or -1 for error
  </ResponseField>

  <ResponseField name="status" type="string" required>
    Migration status (e.g. `"migrating"`, `"done"`, `"error"`)
  </ResponseField>

  <ResponseField name="totalFiles" type="number">
    Total number of files in the migration
  </ResponseField>

  <ResponseField name="currentFileIndex" type="number">
    Index of the file currently being moved
  </ResponseField>

  <ResponseField name="message" type="string">
    Status or error message
  </ResponseField>
</ResponseField>

<ResponseField name="ReplayBufferSaved" type="event">
  Fired when the user saves the replay buffer (typically via the `SaveReplayBuffer` keybind). Emitted with an empty `content` payload — frontends usually surface a toast or sound effect in response.
</ResponseField>

### User Notifications

<ResponseField name="ShowModal" type="event">
  Displays a modal dialog to the user.

  **Content:**

  <ResponseField name="title" type="string" required>
    Modal title
  </ResponseField>

  <ResponseField name="subtitle" type="string">
    Optional subtitle
  </ResponseField>

  <ResponseField name="description" type="string" required>
    Modal body text
  </ResponseField>

  <ResponseField name="type" type="string" required>
    Modal type: "info", "warning", or "error"
  </ResponseField>

  **Example:**

  ```json theme={null}
  {
    "method": "ShowModal",
    "content": {
      "title": "Error",
      "description": "Failed to start recording",
      "type": "error"
    }
  }
  ```
</ResponseField>

<ResponseField name="StorageWarning" type="event">
  Warns user about storage space issues and requests confirmation.

  **Content:**

  <ResponseField name="warningId" type="string" required>
    Unique ID for this warning
  </ResponseField>

  <ResponseField name="title" type="string" required>
    Warning title
  </ResponseField>

  <ResponseField name="description" type="string" required>
    Warning description
  </ResponseField>

  <ResponseField name="confirmText" type="string" required>
    Text for confirm button
  </ResponseField>

  <ResponseField name="cancelText" type="string" required>
    Text for cancel button
  </ResponseField>

  <ResponseField name="action" type="string" required>
    Action to perform if confirmed
  </ResponseField>

  <ResponseField name="actionData" type="any" required>
    Data needed to perform the action
  </ResponseField>
</ResponseField>

<ResponseField name="RecoveryPrompt" type="event">
  Prompts user to recover files after a crash.

  **Content:**

  <ResponseField name="files" type="array" required>
    Array of recoverable file objects
  </ResponseField>

  <ResponseField name="totalCount" type="number" required>
    Total number of files that can be recovered
  </ResponseField>
</ResponseField>

<ResponseField name="BookmarkCreated" type="event">
  Notifies that a bookmark was created (typically from a keybind).

  **Content:** Empty object

  **Example:**

  ```json theme={null}
  {
    "method": "BookmarkCreated",
    "content": {}
  }
  ```
</ResponseField>

### UI Events

<ResponseField name="SelectedGameExecutable" type="event">
  Returns the game executable selected by the user.

  **Content:**

  <ResponseField name="name" type="string" required>
    Game name (derived from executable name)
  </ResponseField>

  <ResponseField name="paths" type="array" required>
    Array containing the selected executable path
  </ResponseField>

  **Example:**

  ```json theme={null}
  {
    "method": "SelectedGameExecutable",
    "content": {
      "name": "MyGame",
      "paths": ["C:/Games/MyGame/game.exe"]
    }
  }
  ```
</ResponseField>

<ResponseField name="ReleaseNotes" type="event">
  Sends release notes for available versions.

  **Content:**

  <ResponseField name="releaseNotesList" type="array" required>
    Array of release note objects

    <ResponseField name="version" type="string" required>
      Version number
    </ResponseField>

    <ResponseField name="base64Markdown" type="string" required>
      Release notes in base64-encoded markdown
    </ResponseField>

    <ResponseField name="releaseDate" type="string" required>
      ISO 8601 date string
    </ResponseField>
  </ResponseField>
</ResponseField>

<ResponseField name="ShowReleaseNotes" type="event">
  Triggers the frontend to display release notes.

  **Content:** Version string to show notes for
</ResponseField>

<ResponseField name="ObsDownloadProgress" type="event">
  Reports OBS download progress during first-time setup.

  **Content:**

  <ResponseField name="progress" type="number" required>
    Download progress (0-100)
  </ResponseField>

  <ResponseField name="status" type="string" required>
    Download status: "downloading"
  </ResponseField>
</ResponseField>

### Heartbeat

<ResponseField name="pong" type="event">
  Response to ping heartbeat message.

  **Content:** Empty object

  **Example:**

  ```json theme={null}
  {
    "method": "pong",
    "content": {}
  }
  ```
</ResponseField>

## Error Handling

### Frontend Validation

Always check if the backend connection is available before sending messages:

```typescript theme={null}
if ((window as any).external && typeof (window as any).external.sendMessage === 'function') {
  sendMessageToBackend('StartRecording');
} else {
  console.error('Backend connection not available');
}
```

### Backend Error Responses

When operations fail, the backend typically:

1. Logs the error with Serilog
2. Sends a progress event with `progress: -1` and an `error` field
3. Optionally shows a `ShowModal` message to the user

**Example error response:**

```json theme={null}
{
  "method": "ClipProgress",
  "content": {
    "id": 123456,
    "progress": -1,
    "segments": [...],
    "error": "FFmpeg not found. Please reinstall Segra."
  }
}
```

### Connection Failures

The WebSocket automatically reconnects if the connection drops:

* **Reconnect attempts:** Infinite
* **Reconnect interval:** 3 seconds
* **Heartbeat timeout:** 30 seconds

When reconnected, the backend re-sends the full state:

```typescript theme={null}
onOpen: () => {
  console.log('WebSocket reconnected - resyncing state');
  sendMessageToBackend('NewConnection');
}
```

## Best Practices

<CardGroup cols={2}>
  <Card title="Use TypeScript Types" icon="shield">
    Import message type definitions from `Models/WebSocketMessages.ts` for type safety.
  </Card>

  <Card title="Handle All States" icon="list-check">
    Progress events can be: in-progress (0-100), complete (100), or error (-1). Handle all cases.
  </Card>

  <Card title="Listen to Events" icon="ear-listen">
    Use `window.addEventListener('websocket-message')` to react to backend events globally.
  </Card>

  <Card title="Graceful Degradation" icon="triangle-exclamation">
    Always check if `window.external.sendMessage` exists before calling it.
  </Card>
</CardGroup>

<Warning>
  Never assume the WebSocket is connected. Use the `isConnected` state from `useWebSocketContext()` to check connection status.
</Warning>

## Example: Complete Clip Workflow

Here's a complete example showing the clip creation workflow:

```typescript theme={null}
import { sendMessageToBackend } from '../Utils/MessageUtils';
import { useEffect, useState } from 'react';

function ClipCreator() {
  const [progress, setProgress] = useState(0);
  const [clipId, setClipId] = useState<number | null>(null);

  // Listen for progress updates
  useEffect(() => {
    const handleMessage = (event: CustomEvent) => {
      const { method, content } = event.detail;
      
      if (method === 'ClipProgress') {
        if (content.id === clipId) {
          if (content.progress === -1) {
            console.error('Clip failed:', content.error);
          } else if (content.progress === 100) {
            console.log('Clip complete!');
          } else {
            setProgress(content.progress);
          }
        }
      }
    };

    window.addEventListener('websocket-message', handleMessage as EventListener);
    return () => window.removeEventListener('websocket-message', handleMessage as EventListener);
  }, [clipId]);

  const createClip = () => {
    const id = Math.floor(Math.random() * 1000000);
    setClipId(id);
    
    sendMessageToBackend('CreateClip', {
      Segments: [{
        id: id,
        startTime: 10,
        endTime: 20,
        fileName: 'gameplay',
        type: 'Session',
        game: 'Valorant',
        title: 'Epic Play'
      }]
    });
  };

  const cancelClip = () => {
    if (clipId) {
      sendMessageToBackend('CancelClip', { id: clipId });
    }
  };

  return (
    <div>
      <button onClick={createClip}>Create Clip</button>
      <button onClick={cancelClip}>Cancel</button>
      <progress value={progress} max={100} />
    </div>
  );
}
```

## Next Steps

<CardGroup cols={2}>
  <Card title="API Overview" icon="book" href="/api/overview">
    Learn about the internal architecture
  </Card>

  <Card title="Core Services" icon="code" href="/api/recording-service">
    Browse backend service documentation
  </Card>
</CardGroup>
