<SYSTEM>This documentation covers how developers might integrate Mux Video with external technologies</SYSTEM>

# Using the Mux MCP Server
Use the Mux Model Context Protocol (MCP) Server to bring Mux's Video and Data platform capabilities directly to your AI tools.
The Mux MCP ([Model Context Protocol](https://modelcontextprotocol.io/introduction)) Server brings Mux's Video and Data platform capabilities directly to your AI tools. Once set up, you can upload videos, manage live streams, analyze video performance, and access practically all of Mux's video infrastructure through natural language prompts in supported AI clients.

This guide walks you through the functionality in Mux's MCP server, and connecting it to various AI clients.

## Tools & Routes

Here are the following tools and API routes supported in the local Mux MCP Server:

* **Video API**: Create assets, uploads, live streams, playback URLs
* **Data API**: Query metrics, dimensions, real-time data
* **Webhook management**: List and verify webhook signatures
* **Asset management**: Retrieve, update video metadata
* **Live streaming**: Create streams, manage recordings
* **Analytics**: Performance metrics, viewer data, error tracking

Here are the tools and routes we don't currently support in the local Mux MCP Server. Generally speaking, these are composed of endpoints which can execute deletions, and are disabled for safety:

* Asset deletion endpoints
* Live stream deletion endpoints
* Webhook deletion endpoints

## Prompt examples

**Video Management**

* Using the Mux tool, create a webpage where I can upload a video to Mux
* Give me the playback URL for the most recently uploaded video to my Mux account, use Mux MCP
* List all my video assets and their current status (using the Mux MCP tool)
* With the Mux tool: Show me recent video uploads
* Using Mux MCP, generate a subtitles track for asset ID: `ASSET_ID`

**Mux Data Analytics and Performance**

* Using the Mux MCP, tell me the best performing country for video streaming over the last month
* Show me video performance metrics for the last week using the Mux tool
* With the Mux tool: what are the top performing videos by view count?
* Using Mux, which countries have the highest video engagement?
* What are the most common video errors in my account (use the Mux MCP)?
* Show me breakdown values for video quality metrics using the Mux MCP tool
* List all available data dimensions I can filter by, use the Mux MCP to answer this prompt

## Prerequisites

Before utilizing the Mux MCP Server, make sure you meet the following prerequisites:

* A Mux account (sign up at [mux.com](https://mux.com/) if you don't have one)
* Claude Desktop, Cursor, or any other client that supports remote MCP servers, installed and updated to the latest version

## Configuring the Mux MCP Server

Mux's MCP server is hosted at https://mcp.mux.com, and when using this remote MCP server, authentication should be handled automatically, with no need for grabbing Access Token information from the Dashboard. In order to configure the Mux MCP server in your client, you need to add an MCP server, which is sometimes called a "connector" (Claude/Claude Code/ChatGPT), an "extension" (Goose), or simply an MCP Server (VSCode), and enter the URL `https://mcp.mux.com` as the location.

Once configured, the LLM client and our MCP server should negotiate authentication and authorization, prompting you automatically to:

* Log in to https://dashboard.mux.com via whatever means you normally log in (this is skipped if you're already logged in)
* Choose which environment you want to authorize this connection for

When you're already logged in, your experience will look something like this:

<Image src="https://image.mux.com/gBuQlI7JC3zQwMlrhSTWPOfEGF02KaVni/animated.gif?start=4&end=11&width=640" width={640} height={640} />

And that's it, you're good to go!

### Configuration options

By default, https://mcp.mux.com will be configured in the simplest manner (though this may change in the future), exposing access to the full set of tools available to Mux. That said, depending on your workflow, you may want to limit this set of tools in some way. For that reason, Mux supports query parameters to configure the MCP server. A more complete set of configuration options can be [seen here](https://github.com/muxinc/mux-node-sdk/tree/master/packages/mcp-server#exposing-endpoints-to-your-mcp-client), and most of those work simply as query params. However, a few bear mentioning directly:

* `tools`: options are `all` (default), and `dynamic`.
  * Use `dynamic` if you want to expose tools mean to [allow the LLMs to dynamically discover endpoints and tools](https://github.com/muxinc/mux-node-sdk/tree/master/packages/mcp-server#exposing-endpoints-to-your-mcp-client), which can aid in controlling context windows and speeding up processing if a lot of tools are available.
* `resource`: array of resources (sets of APIs) to expose, such as `video.*`. These act as an inclusion set, rather than excluding, so you can chain multiple to expand the list of tools. Some options include:
  * `video.*`: all Mux Video APIs
  * `data.*`: all Mux Data APIs
  * `system.*`: all System APIs, such as managing Signing Keys
  * `video.asset.*`: the APIs used to manage Mux Video assets
  * ...and so on
* `client`: options are `claude` (default), `claude-code`, `cursor`, `openai-agents`.
  * Each LLM has varying support for capabilities related to complex JSON schemas, and these are tested defaults for each of the known clients. You can read more about this in [this doc by Stainless](https://www.stainless.com/docs/guides/generate-mcp-server-from-openapi#client-capabilities).

You can also chain these together. For instance, if you want to configure an MCP server that exposes *only* the Video APIs, but does it in a dynamic way, for Cursor, you'd just use `https://mcp.mux.com?client=cursor&resource=video.*&tools=dynamic` as your remote URL.

## A note on remote MCP support

These days, most LLM clients directly support remote MCP servers (rather than locally installed ones), so you shouldn't have much trouble getting set up. That said, there are still some clients (particularly older versions) that don't have built-in remote MCP support (such as Goose as of the time I wrote this guide). For those situations, you have two options:

1. You can still [install our MCP server locally](/docs/integrations/installing-mcp-server-locally)
2. You can utilize [mcp-remote](https://www.npmjs.com/package/mcp-remote), which brings support for remote MCP servers to practically any LLM client (and may perform better than the built-in remote MCP support depending on the client).

## Having trouble?

If you run into issues or have questions:

* Check the [Model Context Protocol documentation](https://modelcontextprotocol.io/quickstart/user) for general MCP setup guidance
* Review [Claude's MCP documentation](https://docs.anthropic.com/en/docs/agents-and-tools/mcp) for Claude-specific configuration
* Visit our [API reference](https://docs.mux.com/api-reference) for detailed endpoint documentation
* Contact support: [mux.com/support](/support)


# Install the local Mux MCP Server
Set up the Mux Model Context Protocol (MCP) Server locally to bring Mux's Video and Data platform capabilities directly to your AI tools.
<Callout type="info">
  If you're interested in getting started quickly, and to read more about the MCP server, check out [this guide](/docs/integrations/mcp-server). This guide walks you through building and installing the Mux MCP Server locally on your machine and connecting it to various AI clients.
</Callout>

The Mux MCP ([Model Context Protocol](https://modelcontextprotocol.io/introduction)) Server brings Mux's Video and Data platform capabilities directly to your AI tools. Once installed, you can upload videos, manage live streams, analyze video performance, and access practically all of Mux's video infrastructure through natural language prompts in supported AI clients.

## Prerequisites

Before installing the Mux MCP Server, make sure you meet the following prerequisites:

* Node.js installed on locally on your machine (instructions available [here](https://nodejs.org/en/download))
* A Mux account (sign up at [mux.com](https://mux.com/) if you don't have one)
* Your Mux API access token and secret key from the [Mux Dashboard](https://dashboard.mux.com/settings/access-tokens) (detailed instructions are available below)
* Claude Desktop, Cursor, or any other client that supports local MCP servers, installed and updated to the latest version

## Installation

### Get your Mux API credentials and configure access

1. Log into your [Mux Dashboard](https://dashboard.mux.com/)
2. Navigate to Settings → Access Tokens
3. Generate a new access token or use an existing one
4. Copy your **Access Token ID** and **Secret Key** - you'll need both for the configuration

#### Required Scopes

* Your Mux access token should be configured for your desired Environment and read/write access
* We recommend clearly labeling this access token in Mux, for example: `MCP Access Token`

**Important:** Replace the placeholder values when adding to your AI client's config using the templates provided below:

* Replace `your_access_token_id` with your actual Mux Access Token ID
* Replace `your_secret_key` with your actual Mux Secret Key

<Callout type="info">
  **Note:** If you're using a tool that manages Node versions like Mise, you'll probably need to make sure you execute the npx commands found in the following examples from within that context. An example Mise command could look something like this:

  `mise x node@20 -- npx -y @mux/mcp@latest`

  Accordingly, the following examples would need to be changed similarly to below:

  ```json
        "command": "mise",
        "args": ["x", "node@20", "--", "npx", "-y", "@mux/mcp@latest","--tools=dynamic","--client=claude"],
  ```
</Callout>

### For Claude

* You must use Claude's Desktop app to install local MCP servers.

We support the recently released [Claude Desktop Extensions](https://www.anthropic.com/engineering/desktop-extensions) format, so you can download [this DXT file](https://github.com/muxinc/mux-node-sdk/releases/download/v12.1.0/mux-mcp.dxt) and open it with Claude Desktop to install it. Once it's installed, configure the environment variables you need and you're good to go.

If you'd like to configure it manually, follow the next steps.

#### Step A: Configure Claude Desktop

Follow [Claude's instructions](https://docs.anthropic.com/en/docs/agents-and-tools/mcp) to locate your Claude Desktop configuration file on your machine.

**macOS/Linux:**

```
~/Library/Application\ Support/Claude/claude_desktop_config.json
```

**Windows:**

```
%APPDATA%\Claude\claude_desktop_config.json
```

#### Step B: Add the MCP Server configuration

Add this configuration block to your `claude_desktop_config.json` file:

```json
{
  "globalShortcut": "",
  "mcpServers": {
    "mux": {
      "command": "npx",
      "args": ["-y", "@mux/mcp@latest","--tools=dynamic","--client=claude"],
      "env": {
        "MUX_TOKEN_ID": "your_access_token_id",
        "MUX_TOKEN_SECRET": "your_secret_key"
      }
    }
  }
}
```

#### Step C: Restart Claude Desktop

Close and reopen Claude Desktop to load the new MCP server configuration.

### For Cursor

#### Step A: Locate the Settings File

Follow the paths below to locate your Cursor MCP configuration file. If the file does not exist, you can create it.

**macOS/Linux:**

```
~/.cursor/mcp.json
```

**Windows:**

```
C:/Users/<username>/.cursor/mcp.json
```

#### Step B: Add the MCP Server Configuration

```json
{
  "mcpServers": {
    "mux": {
      "command": "npx",
      "args": ["-y", "@mux/mcp@latest","--tools=dynamic","--client=cursor"],
      "env": {
        "MUX_TOKEN_ID": "your_access_token_id",
        "MUX_TOKEN_SECRET": "your_secret_key"
      }
    }
  }
}
```

### For VSCode

To add the server to all of your workspaces globally, add the server configuration to your `settings.json` file.

#### Step A: Locate the Settings File

**macOS:**

```
~/Library/Application\ Support/Code/User/settings.json
```

**Linux:**

```
~/.config/Code/User/settings.json
```

**Windows:**

```
%APPDATA%\Code\User\settings.json
```

#### Step B: Add the MCP Server Configuration

```json
{
  "mcp": {
    "servers": {
      "mux": {
        "command": "npx",
        "args": ["-y", "@mux/mcp@latest","--tools=dynamic"],
        "env": {
          "MUX_TOKEN_ID": "your_access_token_id",
          "MUX_TOKEN_SECRET": "your_secret_key"
        }
      }
    }
  }
}
```

#### Step C: Starting the MCP Server

In VSCode, make sure to click on the `Start` button in the MCP Server to start the server. You can do this directly from the settings file, or from the Command Palette with `MCP: List Servers` .

## Verify installation

Test that the Mux MCP Server is working by asking your AI client:

> Give me the details for the most recently created Mux Video asset (using the Mux tool)

or

> Using the Mux MCP, list the best performing countries for video streaming over the last month using Mux Data

If the installation was successful, Claude will connect to the Mux API through the MCP server and return information about your video performance or assets.

## Troubleshooting

**Build Issues**

If you encounter errors during the build process:

* Make sure you have the correct Node.js version installed, and that npx is accessible in your PATH (`npx -v`)

**Connection Issues**

If Claude can't connect to the MCP server:

* Double-check that your file path in the `args` field is correct and points to the built MCP server file
* Verify your Mux credentials are correct and properly formatted
* Make sure there are no extra spaces or characters in your token values
* Confirm your API tokens have the necessary permissions in your Mux account

**Claude Desktop Issues**

If MCP features don't appear in Claude:

* Ensure you're using the latest version of Claude Desktop - older versions may not support MCP
* Verify your JSON configuration is valid (no missing commas or brackets)
* Check that Claude Desktop has restarted completely after configuration changes

## Getting help

If you run into issues or have questions:

* Check the [Model Context Protocol documentation](https://modelcontextprotocol.io/quickstart/user) for general MCP setup guidance
* Review [Claude's MCP documentation](https://docs.anthropic.com/en/docs/agents-and-tools/mcp) for Claude-specific configuration
* Visit our [API reference](https://docs.mux.com/api-reference) for detailed endpoint documentation
* Contact support: [mux.com/support](/support)


# Mux CLI
Use the Mux CLI to manage your video assets, live streams, and more directly from the terminal.
The Mux CLI is a command-line interface for interacting with the Mux API, designed to provide a first-class development experience for working with Mux services locally. With the CLI, you can upload videos, manage live streams, generate signed URLs, query analytics data, and access Mux's video infrastructure without leaving your terminal.

## Installation

The Mux CLI can be installed via Homebrew, npm, a shell installer, or by downloading pre-built binaries.

### Homebrew (macOS)

```bash
brew install muxinc/tap/mux
```

### npm

Install globally to use the `mux` command anywhere:

```bash
npm install -g @mux/cli
```

Or run directly without installing using `npx`:

```bash
npx @mux/cli
```

### Shell installer

Run the install script to automatically download and set up the CLI:

```bash
curl -fsSL https://raw.githubusercontent.com/muxinc/cli/main/install.sh | bash
```

### Binary download

Platform-specific binaries are available for macOS (Apple Silicon and Intel) and Linux (x64 and arm64) from the [GitHub Releases page](https://github.com/muxinc/cli/releases). These are self-contained executables with no external dependencies.

## Shell completions

Enable tab completion for commands, subcommands, and options in your shell:

**Bash** (add to `~/.bashrc`):

```bash
source <(mux completions bash)
```

**Zsh** (add to `~/.zshrc`):

```bash
source <(mux completions zsh)
```

**Fish** (add to `~/.config/fish/config.fish`):

```bash
source (mux completions fish | psub)
```

Restart your shell or source the config file to activate completions.

## Authentication

The CLI requires Mux API credentials to interact with your account. You can get your Access Token ID and Secret Key from the [Mux Dashboard](https://dashboard.mux.com/settings/access-tokens).

### Interactive login

Run `mux login` to authenticate interactively:

```bash
mux login
```

You'll be prompted to enter your Access Token ID and Secret Key. Credentials are stored securely in `~/.config/mux/config.json` with owner-only file permissions.

### Using environment variables

The CLI can read credentials from a `.env` file or environment variables:

```bash
MUX_TOKEN_ID=your-token-id
MUX_TOKEN_SECRET=your-token-secret
```

Login from a `.env` file:

```bash
mux login --env-file .env
```

### Named environments

For managing multiple Mux accounts (production, staging, development), you can configure named environments:

```bash
mux login --name production
mux login --name staging --env-file .env.staging
```

The first environment you add becomes the default. Switch between environments:

```bash
mux env switch staging
mux env list
```

Remove an environment:

```bash
mux logout staging
```

## Common options

These options are available on most commands:

| Option | Description |
|--------|-------------|
| `--json` | Output raw JSON instead of pretty-printed format. Useful for scripting and piping to `jq`. |
| `--compact` | One-line-per-item output, grep-friendly. Available on `list` commands. |
| `--limit <n>` | Number of results to return (default: 25). Available on `list` commands. |
| `--page <n>` | Page number for pagination (default: 1). Available on `list` commands. |
| `-f, --force` | Skip confirmation prompts on destructive actions. |
| `--wait` | Poll until the resource is ready before returning. Available on `create` commands. |

## Webhook forwarding

Listen for Mux webhook events in real-time and forward them to your local development server. Events are stored locally for replay during development.

<Callout type="warning">
  CLI webhook commands are for **local development only** and provide **no delivery guarantees**. In production, you must configure a webhook endpoint in the [Mux Dashboard](https://dashboard.mux.com) that points to your server's webhook URL.
</Callout>

### `mux webhooks listen`

Connect to Mux's event stream and optionally forward events to a local URL.

```bash
# Listen and print events
mux webhooks listen

# Forward to local dev server
mux webhooks listen --forward-to http://localhost:3000/api/webhooks/mux
```

| Option | Description |
|--------|-------------|
| `--forward-to <url>` | POST received events to a local URL in real-time |
| `--json` | Output raw JSON per event |

When using `--forward-to`, the CLI displays a webhook signing secret and signs each forwarded request with a `mux-signature` header. Set `MUX_WEBHOOK_SECRET` in your app's environment to [verify these signatures](/docs/core/verify-webhook-signatures):

```typescript
const event = mux.webhooks.unwrap(body, headers, process.env.MUX_WEBHOOK_SECRET);
```

The signing secret is unique per environment and persisted between sessions, so you only need to configure it once.

### `mux webhooks events list`

List locally stored webhook events captured during `listen` sessions. The CLI stores the last 100 events.

```bash
mux webhooks events list
mux webhooks events list --limit 50
```

### `mux webhooks events replay [event-id]`

Replay stored webhook events. Useful for re-testing your webhook handler without creating new resources.

```bash
# Replay a specific event to your local server
mux webhooks events replay abc123-event-id --forward-to http://localhost:3000/api/webhooks/mux

# Replay all stored events
mux webhooks events replay --all --forward-to http://localhost:3000/api/webhooks/mux

# View event payload without forwarding
mux webhooks events replay abc123-event-id
```

| Option | Description |
|--------|-------------|
| `--forward-to <url>` | POST event(s) to a local URL |
| `--all` | Replay all stored events |
| `--json` | Output JSON instead of pretty format |

### `mux webhooks trigger <event-type>`

Send a synthetic webhook event to a local URL for testing. No API call is made — the payload is generated locally and signed with the per-environment signing secret.

```bash
# Send an example video.asset.ready event
mux webhooks trigger video.asset.ready --forward-to http://localhost:3000/api/webhooks/mux

# Send a live stream event
mux webhooks trigger video.live_stream.active --forward-to http://localhost:3000/api/webhooks/mux
```

| Option | Description |
|--------|-------------|
| `--forward-to <url>` | Local URL to POST the example event to (required) |
| `--json` | Output JSON instead of pretty format |

Run `mux webhooks trigger <invalid-type>` to see all supported event types.

## Commands

### Asset management

Create, list, update, and delete video assets.

```bash
# Create from URL
mux assets create --url https://example.com/video.mp4 --playback-policy public

# Upload local files (glob supported)
mux assets create --upload ./videos/*.mp4 --playback-policy public

# Create from JSON config file (for overlays, subtitles, multiple inputs)
mux assets create --file asset-config.json

# Wait for processing to complete
mux assets create --url https://example.com/video.mp4 --playback-policy public --wait

# List, get, update, delete
mux assets list
mux assets get <asset-id>
mux assets update <asset-id> --title "My Video" --passthrough "my-custom-id"
mux assets delete <asset-id>
```

The interactive asset manager opens a terminal UI (TUI) for browsing and managing your video library:

```bash
mux assets manage
```

<CollapsibleRoot>
  <CollapsibleTrigger>
    View all asset create options
  </CollapsibleTrigger>

  <CollapsibleContent>
    | Option | Description |
    |--------|-------------|
    | `--url <url>` | Video URL to ingest from the web |
    | `--upload <path>` | Local file(s) to upload (supports glob patterns like `*.mp4`) |
    | `--file, -f <path>` | JSON configuration file for complex asset creation |
    | `--playback-policy <policy>` | `public` or `signed` (repeatable) |
    | `--test` | Create test asset (watermarked, 10s limit, deleted after 24h) |
    | `--passthrough <string>` | User metadata (max 255 characters) |
    | `--static-renditions <resolution>` | e.g. `1080p`, `720p`, `highest`, `audio-only` (repeatable) |
    | `--video-quality <quality>` | `basic`, `plus`, or `premium` |
    | `--normalize-audio` | Normalize audio loudness level |
    | `-y, --yes` | Skip confirmation prompts |
  </CollapsibleContent>
</CollapsibleRoot>

<CollapsibleRoot>
  <CollapsibleTrigger>
    View all asset update options
  </CollapsibleTrigger>

  <CollapsibleContent>
    | Option | Description |
    |--------|-------------|
    | `--title <string>` | Set `meta.title` (max 512 characters) |
    | `--creator-id <string>` | Set `meta.creator_id` (max 128 characters) |
    | `--external-id <string>` | Set `meta.external_id` (max 128 characters) |
    | `--passthrough <string>` | Set `passthrough` (max 255 characters) |
  </CollapsibleContent>
</CollapsibleRoot>

<CollapsibleRoot>
  <CollapsibleTrigger>
    Asset sub-resources: playback IDs, static renditions, tracks
  </CollapsibleTrigger>

  <CollapsibleContent>
    #### Playback IDs

    Manage playback IDs on assets. Each asset can have multiple playback IDs with different policies.

    ```bash
    mux assets playback-ids list <asset-id>
    mux assets playback-ids create <asset-id> --policy signed
    mux assets playback-ids delete <asset-id> <playback-id>
    ```

    #### Static renditions

    Static renditions are downloadable MP4 versions of your video assets at specific resolutions.

    ```bash
    mux assets static-renditions list <asset-id>
    mux assets static-renditions create <asset-id> --resolution 1080p --wait
    mux assets static-renditions delete <asset-id> <rendition-id>
    ```

    Resolution options: `highest`, `audio-only`, `2160p`, `1440p`, `1080p`, `720p`, `540p`, `480p`, `360p`, `270p`

    #### Tracks

    Add text and audio tracks (subtitles, captions, audio) to video assets.

    ```bash
    # Add a subtitle track
    mux assets tracks create <asset-id> \
      --url https://example.com/subs.vtt \
      --type text \
      --language-code en \
      --text-type subtitles

    # Generate subtitles from an audio track
    mux assets tracks generate-subtitles <asset-id> <track-id> \
      --language-code en \
      --name "English (auto)"

    # Delete a track
    mux assets tracks delete <asset-id> <track-id>
    ```

    #### Other asset commands

    ```bash
    mux assets input-info <asset-id>                                      # view input file details
    mux assets update-master-access <asset-id> --master-access temporary  # enable master access
    ```
  </CollapsibleContent>
</CollapsibleRoot>

### Live stream management

Create and manage live streams for broadcasting via RTMP.

```bash
# Create a live stream
mux live create --playback-policy public

# Create with options
mux live create \
  --playback-policy public \
  --latency-mode low \
  --reconnect-window 60

# List, get, delete
mux live list
mux live get <live-stream-id>
mux live delete <live-stream-id>

# Stream lifecycle
mux live complete <live-stream-id>
mux live enable <live-stream-id>
mux live disable <live-stream-id>

# Reset stream key
mux live reset-stream-key <live-stream-id>
```

Once created, stream using:

* **RTMP URL:** `rtmp://global-live.mux.com/app`
* **Stream Key:** returned in the response

<CollapsibleRoot>
  <CollapsibleTrigger>
    View all live stream create options
  </CollapsibleTrigger>

  <CollapsibleContent>
    | Option | Description |
    |--------|-------------|
    | `--playback-policy <policy>` | `public` or `signed` (repeatable) |
    | `--new-asset-settings <settings>` | Auto-create asset from stream. Use `none` to disable, or JSON string |
    | `--reconnect-window <seconds>` | Reconnect timeout (default: 60) |
    | `--latency-mode <mode>` | `low`, `reduced`, or `standard` (default: `low`) |
    | `--test` | Create test stream (deleted after 24h) |
  </CollapsibleContent>
</CollapsibleRoot>

<CollapsibleRoot>
  <CollapsibleTrigger>
    View all live stream update options
  </CollapsibleTrigger>

  <CollapsibleContent>
    | Option | Description |
    |--------|-------------|
    | `--latency-mode <mode>` | `low`, `reduced`, or `standard` |
    | `--reconnect-window <seconds>` | Reconnect window (0-1800) |
    | `--max-continuous-duration <seconds>` | Max continuous duration (60-43200) |
    | `--passthrough <string>` | Passthrough metadata (max 255 characters) |
    | `--reconnect-slate-url <url>` | Image to display during reconnect |
    | `--use-slate-for-standard-latency` | Display slate for standard latency streams |
    | `--title <string>` | Title for the live stream |
  </CollapsibleContent>
</CollapsibleRoot>

<CollapsibleRoot>
  <CollapsibleTrigger>
    Live stream sub-resources: simulcast targets, subtitles, playback IDs
  </CollapsibleTrigger>

  <CollapsibleContent>
    #### Simulcast targets

    Restream a live stream to third-party platforms (e.g., YouTube, Twitch).

    ```bash
    mux live simulcast-targets create <stream-id> --url rtmp://live.twitch.tv/app --stream-key live_xxxxx
    mux live simulcast-targets get <stream-id> <target-id>
    mux live simulcast-targets delete <stream-id> <target-id>
    ```

    #### Embedded subtitles (CEA-608)

    ```bash
    mux live update-embedded-subtitles <stream-id> \
      --language-channel cc1 \
      --language-code en \
      --name "English CC"
    ```

    #### Generated subtitles (ASR)

    ```bash
    mux live update-generated-subtitles <stream-id> \
      --language-code en \
      --name "English (auto)"
    ```

    Use `--clear` on either command to remove subtitle settings.

    #### New asset static renditions

    Configure static renditions for assets automatically created from a live stream.

    ```bash
    mux live update-new-asset-static-renditions <stream-id> --resolution 1080p --resolution 720p
    mux live delete-new-asset-static-renditions <stream-id>
    ```

    #### Playback IDs

    ```bash
    mux live playback-ids list <stream-id>
    mux live playback-ids create <stream-id> --policy signed
    mux live playback-ids delete <stream-id> <playback-id>
    ```
  </CollapsibleContent>
</CollapsibleRoot>

### Direct uploads

Create direct upload URLs for client-side video uploading.

```bash
mux uploads create --cors-origin "https://example.com" --playback-policy public
mux uploads list
mux uploads get <upload-id>
mux uploads cancel <upload-id>
```

| Option | Description |
|--------|-------------|
| `--cors-origin <origin>` | Allowed CORS origin for the upload (required) |
| `-p, --playback-policy <policy>` | `public` or `signed` |
| `--timeout <seconds>` | Seconds before the upload times out (default: 3600) |
| `--test` | Create a test upload (asset deleted after 24 hours) |

### Signing keys and secure playback

For assets with signed playback policies, the CLI can generate secure URLs.

#### Create a signing key

```bash
mux signing-keys create
```

<Callout type="warning">
  The private key is only returned once during creation. The CLI automatically stores it in your current environment configuration.
</Callout>

```bash
mux signing-keys list
mux signing-keys get <key-id>
mux signing-keys delete <key-id>
```

#### Generate signed URLs

```bash
# Sign for video playback
mux sign <playback-id>

# Sign with custom expiration
mux sign <playback-id> --expiration 24h

# Sign a thumbnail with parameters
mux sign <playback-id> --type thumbnail --param time=14 --param width=100

# Sign a GIF
mux sign <playback-id> --type gif

# Output token only (no URL)
mux sign <playback-id> --token-only

# Pass JWT claims as JSON
mux sign <playback-id> --params-json '{"custom": {"session_id": "xxxx-123"}}'
```

| Option | Description |
|--------|-------------|
| `-e, --expiration <duration>` | Token expiration (default: `7d`). Examples: `7d`, `24h`, `30m` |
| `-t, --type <type>` | `video` (default), `thumbnail`, `gif`, `storyboard` |
| `-p, --param <key=value>` | JWT claim as key=value (repeatable) |
| `--params-json <json>` | JWT claims as JSON object |
| `--token-only` | Output only the JWT token |

<CollapsibleRoot>
  <CollapsibleTrigger>
    View thumbnail parameters
  </CollapsibleTrigger>

  <CollapsibleContent>
    These parameters can be passed via `--param` when using `--type thumbnail`:

    | Parameter | Description |
    |-----------|-------------|
    | `time` | Video timestamp in seconds |
    | `width` | Width in pixels |
    | `height` | Height in pixels |
    | `rotate` | Clockwise rotation: 90, 180, or 270 |
    | `fit_mode` | `preserve`, `stretch`, `crop`, `smartcrop`, `pad` |
    | `flip_v` | Flip vertically |
    | `flip_h` | Flip horizontally |
  </CollapsibleContent>
</CollapsibleRoot>

### Playback ID lookup

Look up which asset or live stream a playback ID belongs to:

```bash
mux playback-ids <playback-id>
mux playback-ids <playback-id> --expand  # fetch the full asset or live stream object
```

### Playback restrictions

Control where and how your content can be played.

```bash
# Create a restriction
mux playback-restrictions create \
  --allowed-domains "example.com" \
  --allowed-domains "*.example.com"

# List, get, delete
mux playback-restrictions list
mux playback-restrictions get <restriction-id>
mux playback-restrictions delete <restriction-id>

# Update referrer rules
mux playback-restrictions update-referrer <restriction-id> \
  --allowed-domains "example.com" \
  --allow-no-referrer

# Update user agent rules
mux playback-restrictions update-user-agent <restriction-id> \
  --allow-no-user-agent true \
  --allow-high-risk-user-agent false
```

### Transcription vocabularies

Manage custom vocabularies to improve automatic speech recognition accuracy for domain-specific terms.

```bash
# Create a vocabulary
mux transcription-vocabularies create \
  --phrase "Mux" --phrase "HLS" --phrase "RTMP" \
  --name "Streaming Terms"

# List, get, update, delete
mux transcription-vocabularies list
mux transcription-vocabularies get <vocabulary-id>
mux transcription-vocabularies update <vocabulary-id> --phrase "Mux" --phrase "DASH"
mux transcription-vocabularies delete <vocabulary-id>
```

### Delivery usage

List delivery usage reports for video assets and live streams.

```bash
mux delivery-usage list
mux delivery-usage list --asset-id <id>
mux delivery-usage list --live-stream-id <id>
```

### DRM configurations

View DRM configurations for your Mux environment. DRM configurations are provisioned by Mux and are read-only.

```bash
mux drm-configurations list
mux drm-configurations get <drm-configuration-id>
```

### Mux Data

Commands for video analytics, monitoring, and incident tracking via the Mux Data API.

#### Video views

```bash
mux video-views list
mux video-views list --filters "country:US" --timeframe "24:hours"
mux video-views list --viewer-id <id>
mux video-views get <view-id>
```

#### Metrics

```bash
# List available metrics
mux metrics list

# Breakdown by dimension
mux metrics breakdown <metric-id> --group-by country --measurement median

# Overall metric values
mux metrics overall <metric-id> --measurement avg

# Timeseries data
mux metrics timeseries <metric-id> --group-by hour

# Performance insights
mux metrics insights <metric-id> --measurement 95th
```

Common options for metric commands: `--measurement <95th|median|avg|count|sum>`, `--filters`, `--metric-filters`, `--timeframe`

#### Monitoring

Real-time monitoring data from Mux Data.

```bash
mux monitoring dimensions
mux monitoring metrics
mux monitoring breakdown <metric-id> --dimension <d>
mux monitoring breakdown-timeseries <metric-id> --dimension <d>
mux monitoring histogram-timeseries --filters ...
mux monitoring timeseries <metric-id>
```

#### Incidents

```bash
mux incidents list
mux incidents list --status open --severity alert
mux incidents get <incident-id>
mux incidents related <incident-id>
```

#### Annotations

Mark significant events (deployments, config changes) on your analytics timeline.

```bash
mux annotations create --date 1700000000 --note "Deployed v2.1.0"
mux annotations list
mux annotations get <annotation-id>
mux annotations update <annotation-id> --date <timestamp> --note <text>
mux annotations delete <annotation-id>
```

#### Dimensions, errors, and exports

```bash
# List available dimensions and their values
mux dimensions list
mux dimensions values <dimension-id> --timeframe "24:hours"

# List errors
mux errors list --filters ... --timeframe ...

# List video view export files
mux exports list
```

## Getting help

View available commands:

```bash
mux --help
```

Get help for a specific command:

```bash
mux assets --help
mux assets create --help
```

## Full documentation

For more information, see the [Mux CLI repository](https://github.com/muxinc/cli) on GitHub.


# Add high-performance video to your Node application
Use our API and components to handle embedding, storing, and streaming video in your Node application
## Installation

Add a dependency on the `@mux/mux-node` package via npm or yarn.

```bash
npm install @mux/mux-node
```

## Quickstart

To start, you'll need a Mux access token. Once you've got that, you're off to the races!

```javascript
import Mux from '@mux/mux-node';
const mux = new Mux({
  tokenId: process.env.MUX_TOKEN_ID,
  tokenSecret: process.env.MUX_TOKEN_SECRET
});

const asset = await mux.video.assets.create({
  input: [{ url: 'https://storage.googleapis.com/muxdemofiles/mux-video-intro.mp4' }],
  playback_policy: ['public'],
});
```

## Full documentation

Check out the [Mux Node SDK docs](https://github.com/muxinc/mux-node-sdk) for more information.


# Add high-performance video to your Python application
Use our API and components to handle embedding, storing, and streaming video in your Python application
## Installation

Install this module using either `pip` or by installing from source.

```curl
# Via pip
pip install git+https://github.com/muxinc/mux-python.git

# Via source
git checkout https://github.com/muxinc/mux-python.git
cd mux-python
python setup.py install --user
```

## Quickstart

To start, you'll need a Mux access token. Once you've got that, you're off to the races!

```python
import os
import mux_python
from mux_python.rest import ApiException

# Authentication Setup
configuration = mux_python.Configuration()
configuration.username = os.environ['MUX_TOKEN_ID']
configuration.password = os.environ['MUX_TOKEN_SECRET']

# API Client Initialization
assets_api = mux_python.AssetsApi(mux_python.ApiClient(configuration))

# List Assets
print("Listing Assets: \n")
try:
    list_assets_response = assets_api.list_assets()
    for asset in list_assets_response.data:
        print('Asset ID: ' + asset.id)
        print('Status: ' + asset.status)
        print('Duration: ' + str(asset.duration) + "\n")
except ApiException as e:
    print("Exception when calling AssetsApi->list_assets: %s\n" % e)
```

## Full documentation

Check out the [Mux Python SDK docs](https://github.com/muxinc/mux-python) for more information.


# Add high-performance video to your PHP application
Use our API and components to handle embedding, storing, and streaming video in your PHP application
## Installation

We publish Mux PHP to Packagist. You should depend on Mux PHP by adding us to your `composer.json` file.

```php
composer require mux/mux-php
```

## Quickstart

To start, you'll need a Mux access token. Once you've got that, you're off to the races!

```php
// Authentication Setup
$config = MuxPhp\Configuration::getDefaultConfiguration()
    ->setUsername(getenv('MUX_TOKEN_ID'))
    ->setPassword(getenv('MUX_TOKEN_SECRET'));

// API Client Initialization
$assetsApi = new MuxPhp\Api\AssetsApi(
    new GuzzleHttp\Client(),
    $config
);

// Create Asset Request
$input = new MuxPhp\Models\InputSettings(["url" => "https://storage.googleapis.com/muxdemofiles/mux-video-intro.mp4"]);
$createAssetRequest = new MuxPhp\Models\CreateAssetRequest(["input" => $input, "playback_policy" => [MuxPhp\Models\PlaybackPolicy::PUBLIC_PLAYBACK_POLICY] ]);

// Ingest
$result = $assetsApi->createAsset($createAssetRequest);

// Print URL
print "Playback URL: https://stream.mux.com/" . $result->getData()->getPlaybackIds()[0]->getId() . ".m3u8\n"
```

## Full documentation

Check out the [Mux PHP SDK docs](https://github.com/muxinc/mux-php) for more information.


# Add high-performance video to your Ruby application
Use our API and components to handle embedding, storing, and streaming video in your Ruby application
## Installation

Add `mux_ruby` to your project's `Gemfile`.

```ruby
gem 'mux_ruby'
```

## Quickstart

To start, you'll need a Mux access token. Once you've got that, you're off to the races!

```ruby
require 'mux_ruby'

# Auth Setup
openapi = MuxRuby.configure do |config|
  config.username = ENV['MUX_TOKEN_ID']
  config.password = ENV['MUX_TOKEN_SECRET']
end

# API Client Init
assets_api = MuxRuby::AssetsApi.new

# List Assets
puts "Listing Assets in account:\n\n"

assets = assets_api.list_assets()
assets.data.each do | asset |
  puts "Asset ID: #{asset.id}"
  puts "Status: #{asset.status}"
  puts "Duration: #{asset.duration.to_s}\n\n"
end
```

## Full documentation

Check out the [Mux Ruby SDK docs](https://github.com/muxinc/mux-ruby) for more information.


# Add high-performance video to your Elixir application
Use our API and components to handle embedding, storing, and streaming video in your Elixir application
## Installation

Add `mux` to your list of dependencies in `mix.exs`.

```elixir
def deps do
  [
    {:mux, "~> 3.2.1"}
  ]
end
```

## Quickstart

To start, we'll need a Mux access token. We'll put our access token in our application configuration.

```elixir
# config/dev.exs
config :mux,
  access_token_id: "abcd1234",
  access_token_secret: "efghijkl"
```

Then use this config to initialize a new client in your application.

```elixir
client = Mux.client()
```

You can also pass the access token ID and secret directly to client/2 function if you'd prefer:

```elixir
client = Mux.client("access_token_id", "access_token_secret")
```

Now we can use the client to upload new videos, manage playback IDs, etc.

```elixir
params = %{
  input: "https://example.com/video.mp4"
}
{:ok, asset, _} = Mux.Video.Assets.create(client, params);
```

## Full documentation

Check out the [Mux Elixir SDK docs](https://github.com/muxinc/mux-elixir) for more information.


# Add high-performance video to your Java application
Use our API and components to handle embedding, storing, and streaming video in your Java application
## Installation

There are several ways to add the Mux Java SDK to your project:

### Maven

Add this dependency to your project's POM:

```xml
<dependency>
  <groupId>com.mux</groupId>
  <artifactId>mux-sdk-java</artifactId>
  <version>1.0.0</version>
  <scope>compile</scope>
</dependency>
```

### Gradle

Add this dependency to your project's build file:

```gradle
compile "com.mux:mux-sdk-java:1.0.0"
```

## Quickstart

To start, you'll need a Mux access token. Once you've got that, you're off to the races!

```java
// Import classes:
import com.mux.ApiClient;
import com.mux.ApiException;
import com.mux.Configuration;
import com.mux.auth.*;
import com.mux.models.*;
import com.mux.sdk.AssetsApi;

public class Example {
  public static void main(String[] args) {
    ApiClient defaultClient = Configuration.getDefaultApiClient();
    defaultClient.setBasePath("https://api.mux.com");
    
    // Configure HTTP basic authorization: accessToken
    HttpBasicAuth accessToken = (HttpBasicAuth) defaultClient.getAuthentication("accessToken");
    accessToken.setUsername("YOUR USERNAME");
    accessToken.setPassword("YOUR PASSWORD");

    AssetsApi apiInstance = new AssetsApi(defaultClient);
    CreateAssetRequest createAssetRequest = {"input":[{"url":"https://muxed.s3.amazonaws.com/leds.mp4"}],"playback_policy":["public"],"video_quality":"basic"}; // CreateAssetRequest | 
    try {
      AssetResponse result = apiInstance.createAsset(createAssetRequest)
            .execute();
      System.out.println(result);
    } catch (ApiException e) {
      System.err.println("Exception when calling AssetsApi#createAsset");
      System.err.println("Status code: " + e.getCode());
      System.err.println("Reason: " + e.getResponseBody());
      System.err.println("Response headers: " + e.getResponseHeaders());
      e.printStackTrace();
    }
  }
}

```

## Full documentation

Check out the [Mux Java SDK docs](https://github.com/muxinc/mux-java) for more information.


# Add high-performance video to your C# application
Use our API and components to handle embedding, storing, and streaming video in your C# application
## Frameworks supported

* .NET Core >=1.0
* .NET Framework >=4.6
* Mono/Xamarin >=vNext

## Dependencies

* [RestSharp](https://www.nuget.org/packages/RestSharp) - 106.11.4 or later
* [Json.NET](https://www.nuget.org/packages/Newtonsoft.Json/) - 12.0.3 or later
* [JsonSubTypes](https://www.nuget.org/packages/JsonSubTypes/) - 1.7.0 or later
* [System.ComponentModel.Annotations](https://www.nuget.org/packages/System.ComponentModel.Annotations) - 4.7.0 or later

The DLLs included in the package may not be the latest version. We recommend using [NuGet](https://docs.nuget.org/consume/installing-nuget) to obtain the latest version of the packages:

```
Install-Package RestSharp
Install-Package Newtonsoft.Json
Install-Package JsonSubTypes
Install-Package System.ComponentModel.Annotations
```

NOTE: RestSharp versions greater than 105.1.0 have a bug which causes file uploads to fail. See [RestSharp#742](https://github.com/restsharp/RestSharp/issues/742)

## Installation

Generate the DLL using your preferred tool (e.g. `dotnet build`)

Then include the DLL (under the `bin` folder) in the C# project, and use the namespaces:

```csharp
using Mux.Csharp.Sdk.Api;
using Mux.Csharp.Sdk.Client;
using Mux.Csharp.Sdk.Model;
```

## Usage

<Callout type="warning" title="Usage With Webhooks">
  At this moment, this SDK is not suitable for parsing or modeling webhook payloads, due to some incompatibilities in our API spec and our SDK generation tooling. We are working on resolving these issues, but for now you should only use this SDK for Mux's REST APIs.
</Callout>

To use the API client with a HTTP proxy, setup a `System.Net.WebProxy`

```csharp
Configuration c = new Configuration();
System.Net.WebProxy webProxy = new System.Net.WebProxy("http://myProxyUrl:80/");
webProxy.Credentials = System.Net.CredentialCache.DefaultCredentials;
c.Proxy = webProxy;
```

## Getting Started

```csharp
using System.Collections.Generic;
using System.Diagnostics;
using Mux.Csharp.Sdk.Api;
using Mux.Csharp.Sdk.Client;
using Mux.Csharp.Sdk.Model;

namespace Example
{
    public class Example
    {
        public static void Main()
        {

            Configuration config = new Configuration();
            config.BasePath = "https://api.mux.com";
            // Configure HTTP basic authorization: accessToken
            config.Username = "YOUR_USERNAME";
            config.Password = "YOUR_PASSWORD";

            var apiInstance = new AssetsApi(config);
            var createAssetRequest = new CreateAssetRequest(); // CreateAssetRequest | 

            try
            {
                // Create an asset
                AssetResponse result = apiInstance.CreateAsset(createAssetRequest);
                Debug.WriteLine(result);
            }
            catch (ApiException e)
            {
                Debug.Print("Exception when calling AssetsApi.CreateAsset: " + e.Message );
                Debug.Print("Status Code: "+ e.ErrorCode);
                Debug.Print(e.StackTrace);
            }

        }
    }
}
```

## Full documentation

Check out the [Mux C# SDK docs](https://github.com/muxinc/mux-csharp) for more information.


# Integrate with your CMS
Mux has a growing collection of CMS integrations that make it easy to incorporate your Mux video details into your application.
While Mux stores basic video metadata like titles and creator IDs, a content management system (CMS) can help you manage additional content around your videos - like descriptions, categories, related content, and other rich metadata that helps organize your video content within your application's broader content strategy.

Mux integrates with the following CMS applications to help you easily manage and deliver video content in your applications. These integrations allow you to easily incorporate Mux videos into your existing CMS workflow.

## Headless CMS integrations

* [Sanity](/docs/integrations/sanity)
* [Contentful](/docs/integrations/contentful)
* [WordPress](/docs/integrations/wordpress)
* [Strapi](/docs/integrations/strapi)
* [Cosmic](/docs/integrations/cosmic)
* [DatoCMS](/docs/integrations/datocms)
* [Prepr](/docs/integrations/prepr)

## Third-party integrations

These are integrations that are not officially supported by Mux, but are community-driven and maintained.

* [PayloadCMS](https://github.com/oversightstudio/payload-plugins/tree/main/packages/mux-video)
* [Statamic](https://github.com/daun/statamic-mux)

If there’s an integration you’d like to see or if you’d like to partner with us, [let us know](mailto:info@mux.com)!


# Integrate with Sanity
Learn how to integrate Mux video with your Sanity studio. If your team is using Sanity as a CMS this integration will allow them to upload videos to Mux without leaving the Sanity studio.
<Callout type="info" title="Prerequisites">
  This guide assumes you already have a Sanity Studio set up. If you haven't created your Sanity Studio yet, follow the [Sanity Studio quickstart guide](https://www.sanity.io/docs/sanity-studio-quickstart/setting-up-your-studio) to get started.
</Callout>

## 1. Install Mux plugin

Run this command in your Sanity project folder:

```sh
npm i sanity-plugin-mux-input
```

## 2. Use in a schema

To use Mux video in your Sanity schemas, you'll need to create a schema type, import it to your schema types index, and configure the Mux plugin in your Sanity configuration file.

### 2.1. Create a schema type

Create a new file in your `schemaTypes` directory (or `schemas` directory, depending on your setup). For example, create a file called `videoBlogPost.ts`:

```typescript
// schemaTypes/videoBlogPost.ts
import { defineType, defineField } from 'sanity'

export default defineType({
  title: 'Video blog post',
  name: 'videoBlogPost',
  type: 'document',
  fields: [
    defineField({
      name: 'title',
      type: 'string',
      title: 'Title'
    }),
    defineField({
      name: 'video',
      type: 'mux.video',
      title: 'Video file'
    })
  ]
})
```

### 2.2. Import the schema type

Import your new schema type in your schema types index file (usually `schemaTypes/index.ts` or `schemas/index.ts`):

```typescript
// schemaTypes/index.ts
import videoBlogPost from './videoBlogPost'

export const schemaTypes = [videoBlogPost]
```

### 2.3. Configure the Mux plugin

Add the Mux plugin to your Sanity configuration file (`sanity.config.ts` or `sanity.config.js`):

```typescript
// sanity.config.ts
import { defineConfig } from 'sanity'
import { structureTool } from 'sanity/structure'
import { muxInput } from 'sanity-plugin-mux-input'
import { schemaTypes } from './schemaTypes'

export default defineConfig({
  name: 'default',
  title: 'My Sanity Project',
  
  projectId: 'your-project-id',
  dataset: 'production',
  
  plugins: [
    structureTool(),
    muxInput()
  ],
  
  schema: {
    types: schemaTypes,
  },
})
```

## 3. Enter Mux credentials

Generate a new Access Token by going to the Access Token settings of your Mux account dashboard.

<Image src="/docs/images/settings-api-access-tokens.png" width={500} height={500} />

The access token should have Mux Video Read and Write permissions as well as Mux Data (read-only).
If you want to use signed or DRM playback, you need to enable both **Read** and **Write** permissions for the `System` section. For more information, check out the [Signed Tokens](/docs/integrations/sanity#signed-tokens) section.

<Image src="/docs/images/new-cms-token.png" width={608} height={480} alt="Mux Video and Mux Data access token permissions" sm />

Back in Sanity Studio, navigate to the **Videos** section in your studio menu, then click on **Configure plugin**. Enter your Access Token ID and Secret Key in the configuration settings.

<Image src="/docs/images/sanity-configure-plugin.png" width={800} height={400} />

You'll also see an option to **Enable signed URLs**. This feature allows you to create videos with signed playback policies for additional security. If you're unsure, you can leave this disabled for now—you can learn more about this feature in the [Signed Tokens](#signed-tokens) section below.

Additionally, you will see an input to add an optional **DRM Configuration ID**. This feature allows you to create videos with an extra layer of security using DRM. You can learn more about this feature in the [DRM](#digital-rights-management-drm) section below.

## 4. Upload video

Use the select button to open the file explorer on your system, drag the file right into the input area, or paste the URL to the video in the field. Once it's done uploading, you can select the thumbnail you want for the preview.

<Player playbackId="TEgRQ00yGgc6GflbsK4Z44HwZDMIxKqY1" thumbnailTime="0" title="Sanity - Mux Video input - Upload" />

<Callout type="success" title="Congratulations!">
  You now have the ability to upload content to Mux through Sanity CMS!
</Callout>

To retrieve your video for playback, check out the [Sanity docs](https://www.sanity.io/blog/first-class-responsive-video-support-with-the-new-mux-plugin) for instructions.

## 5. Explore advanced options

## Signed Tokens

<Callout type="warning" title="Warning! Requires generating JWT on your server">
  Enabling signed URLs in Sanity will require you to generate your own signing tokens on your application **server**. This involves creating a signing key and using that to generate JSON web tokens when you want to access your videos and thumbnails outside of Sanity.
</Callout>

By default, all assets uploaded to Mux through Sanity will be created with a playback policy of `"public"`. This means that your videos and thumbnails are accessible with `https://stream.mux.com/{PLAYBACK_ID}.m3u8` and `https://image.mux.com/{PLAYBACK_ID}/thumbnail.jpg`.

If you want more control over delivery of the playback and thumbnail access, you can enable this feature on the Sanity configuration popover:

<Image src="/docs/images/sanity-signed-urls.png" width={852} height={640} sm />

When you enable this feature, the following things will happen:

1. The Mux Plugin in Sanity will use the Mux API to create a URL signing key and save this with your `secrets` document.
2. Any assets that get created while this feature is enabled will be created with `playback_policy: "signed"` (instead of `"public"`).
3. The signing key from Step 1 will be used by the Mux Plugin to preview content inside the Sanity UI.
4. When you access your content in your own application, use the `MuxAsset.data.playback_ids` property to determine if the asset has a `signed` or `public` policy.

```json
{
  "_id": "0779365f-bbd1-46ab-9d78-c55feeb28faa",
  "_type": "mux.videoAsset",
  "assetId": "fNMFNYMq48EwgJM7AIn1rNldiFBcVIdK",
  "data": {
    "playback_ids": [
      {
        "id": "01cBJKm5KoeQii00YYGU7Rvpzvh6V01l4ZK",
        "policy": "public"
      }
    ]
  },
  "status": "ready"
}
```

5. You should use the signed `playbackId` to create URLs for playback and for thumbnail generation.

* Playback `https://stream.mux.com/{SIGNED_PLAYBACK_ID}.m3u8?token={TOKEN}`
* Thumbnails `https://image.mux.com/{SIGNED_PLAYBACK_ID}/thumbnail.jpg?token={TOKEN}`

6. The `TOKEN` parameter for the above URLs is something you create on your server according to Step 2 in [Secure video playback](/docs/guides/secure-video-playback)

Note that in the Sanity UI when an asset is using a signed URL you will see this green notice.

<Image src="/docs/images/sanity-signed-playback.png" width={562} height={783} sm />

## Digital Rights Management (DRM)

**Note:**
Enabling DRM in Sanity will require you to generate your own signing tokens on your application **server**. This involves creating a signing key and using that to generate JSON web tokens when you want to access your videos and thumbnails outside of Sanity.

### Prerequisites

Before you can use DRM playback policies, you'll need:

* An access token with Read and Write permissions for the System section. See how to do this in [step 3](#3-enter-mux-credentials).
* DRM support enabled in your Mux account and the respective DRM Configuration ID. You can see the steps to enable DRM for your account in our [DRM guide](/docs/guides/protect-videos-with-drm).

To enable DRM on Sanity, simply add your DRM Configuration ID on the configuration popover.
If you want to preview DRM-protected content in Sanity, make sure you also enable Signed URLs.

<Image src="/docs/images/sanity-drm-configuration-popver.png" width={562} height={783} sm />

### Uploading DRM-protected assets

Once DRM is enabled on Sanity, to upload a new asset with DRM protection:

* Set Video Quality to Plus or Premium
* Look for the DRM option in the Playback Policy section
* Enable the DRM checkbox to upload your asset with DRM protection

<Image src="/docs/images/sanity-drm-upload-example.png" width={562} height={783} sm />

If you don't see the DRM option or it appears disabled, make sure that you're on the latest version of the plugin and you've added your DRM Configuration ID in the plugin settings.

### Multiple playback policies

You can select multiple playback policies for a single asset:

* Public: Anyone with the playback URL can view the video
* Signed: Requires a signed JWT token to view the video
* DRM: Protected content that prevents screen recording and screenshots

Combining policies gives you flexibility in how you distribute your content.
However, note that when multiple policies are enabled, each playback ID maintains its own
protection level. For example, a Public playback ID will remain accessible without
protection even if DRM is also enabled.

When viewing an asset's details, the panel displays all selected playback policies for
that asset and the corresponding playback ID for each policy.

<Image src="/docs/images/sanity-multi-policy-details-example.png" width={562} height={783} sm />

### Viewing DRM-protected videos

#### Videos tab

In the Videos tab, assets with DRM protection are indicated with a special DRM icon.

<Image src="/docs/images/sanity-drm-icon-videos-view.png" width={300} height={600} sm />

* Thumbnails will still be visible (they are not DRM-protected)
* The Sanity video player will automatically use the most appropriate playback policy while displaying the icon for the asset's strongest policy:
  1. Public playback is prioritized (no additional cost)
  2. Signed playback is used if public isn't available
  3. DRM playback is used only when necessary (as it incurs a small cost per playback)

This approach helps minimize playback costs while still indicating the protection level of your assets.

### Advanced configuration

#### Default value for DRM policy

DRM policy is disabled by default. You can use `publicDrm: true` to enable DRM policy for all new assets.

```js
muxInput({
  // ... other Mux plugin config
  defaultDrm: true
})
```

Note that this will only apply when a DRM Configuration ID is present.

#### Disable DRM playback warning

The first time you play DRM-protected content, you'll see a warning message explaining the behavior of DRM playback.
This message is shown only once and tracked using local storage with the key `mux-plugin-has-shown-drm-playback-warning`.

To disable this warning entirely, you can add `disableDrmPlaybackWarning: true` to the plugin configuration:

```js
muxInput({
  // ... other Mux plugin config
  disableDrmPlaybackWarning: true
})
```

### Troubleshooting

#### DRM checkbox is disabled

If the DRM playback policy option appears disabled, verify that you've added a DRM Configuration ID in the plugin settings.

<Image src="/docs/images/sanity-drm-policy-disabled.png" width={600} height={783} sm />

#### Invalid DRM Configuration ID

The plugin doesn't validate your DRM Configuration ID in advance.
If you enter an invalid ID, you may encounter errors when attempting to upload DRM-protected assets.
Make sure you're using the correct ID from your Mux dashboard
and that your access token was created with Read and Write System permissions.

#### My DRM Configuration ID is set, but I cannot play back DRM-protected content.

DRM playback uses a playback token just like Signed Playback. Make sure that `"Enable Signed URLs"` is checked in the Plugin configuration popover.

## Video quality

**Note:**
We recently renamed encoding tiers to video quality levels. [Read the blog](https://www.mux.com/blog/no-one-said-naming-was-easy-encoding-tiers-are-now-video-quality-levels) for more details.

When uploading a new video, you can select which Video Quality is used when preparing the Asset. Possible selections are `Premium`, `Plus` and `Basic`. When choosing `Premium` or `Plus`, additional options are made available for maximum resolutions (1080p, 2K or 4K).

More details can be found in our [Use Video Quality](/docs/guides/use-video-quality-levels) guide.

## Static Renditions

When using the `Plus` Video Quality, an option to enable downloadable MP4s will be available. This option will create [Static Renditions](/docs/guides/enable-static-mp4-renditions) for the Asset and will make MP4 files available for download to client devices using a formatted URL.

## Max Video Resolution

You can specify the maximum resolution to encode the uploaded video. This option is particularly important in managing costs when uploaded videos are higher than `1080p` resolution and also allows you to encode and play videos in 2k or 4k resolutions.
More information on the feature is available in [our docs](/docs/guides/stream-videos-in-4k). Also, read more on this feature announcement in our [blog post](https://www.mux.com/blog/more-pixels-fewer-problems-introducing-4k-support-for-mux-video).

## Watermarks

You can add a watermark image overlay to videos uploaded through Sanity. When uploading a new video, the plugin provides a watermark configuration section where you can specify a watermark image URL (PNG or JPG) and customize its appearance.

<Image src="/docs/images/sanity-watermark-input.png" width={605} height={153} />

The watermark settings include:

* **Image URL**: A URL pointing to a PNG or JPG image to use as the watermark
* **Size**: Control the dimensions of the watermark on the video
* **Opacity**: Adjust the transparency of the watermark
* **Position**: Place the watermark using either a visual canvas-based drag interface or by manually editing the position values

<Image src="/docs/images/sanity-watermark-canvas.png" width={614} height={625} sm />

**Note:** Watermark settings only apply to video uploads. The watermark configuration UI will not appear for audio-only files.

For more details on watermark positioning options (percent vs. pixel values, centering, and multiple watermarks), see the [Add watermarks to your videos](/docs/guides/add-watermarks-to-your-videos) guide.

## Captions and Subtitles

You can add captions to your videos in two ways: during the initial upload or after the video has been uploaded. Both auto-generated and custom captions are supported, and you can use both types on the same asset.

### Adding captions during upload

When uploading a new video, you can configure auto-generated captions in the upload modal before the file is uploaded to Mux. This allows you to set up auto-generated captions right from the start.

<Image src="/docs/images/sanity-captions-1.png" width={500} height={700} />

### Adding captions to existing videos

For videos that have already been uploaded, you can add or manage captions in two ways:

* **From the Videos section:** Go to **Videos** in your studio menu, find the video in the list, and open it to view its details and caption options.
* **From the document:** Open the document that contains the video field, click the three-dots menu on the video input, then select **Captions**.

<Image src="/docs/images/sanity-captions-2.png" width={1200} height={500} />

### Types of captions

#### Auto-generated captions

For auto-generated captions, select the language of the spoken audio in the video. Mux will generate the captions automatically while it prepares the asset. The display name you choose is what will appear in the player when users select the caption track.

<Callout type="warning" title="Auto-generate a single caption track">
  The auto-generated option should only be used to generate one caption track per asset. The language selected must match the spoken language in the video.
</Callout>

<Image src="/docs/images/sanity-captions-3.png" width={1200} height={500} />

More details: [Add auto-generated captions and use transcripts](/docs/guides/add-autogenerated-captions-and-use-transcripts).

#### Custom captions

You can add custom captions and subtitles by providing a public URL to a `.vtt` or `.srt` file. Enter the URL in the caption configuration and set the caption name and language. You can host the file in Sanity's Media Library or any other public URL.

<Image src="/docs/images/sanity-captions-4.png" width={1200} height={500} />

More details: [Add subtitles/captions to videos](/docs/guides/add-subtitles-to-your-videos).

### Managing captions

Caption tracks can be added and removed at any time. Changes are reflected in the stored asset data. If you need to edit auto-generated captions, you can download the VTT file, make your edits, and re-upload it as a custom caption.

<GuideCard title="Set up playback" description="Set up your iOS application, Android application or web application to start playing your Mux assets" links={[{ title: 'Read the guide', href: '/docs/guides/play-your-videos' }]} />

<GuideCard title="Preview your video" description="Now that you have Mux assets, build rich experiences into your application by extracting images from your videos" links={[{ title: 'Read the guide', href: '/docs/guides/get-images-from-a-video' }]} />

<GuideCard title="Integrate Mux Data" description="Add the Mux Data SDK to your player and start collecting playback performance metrics." links={[{ title: 'Read the guide', href: '/docs/guides/track-your-video-performance' }]} />


# Integrate with Contentful
The Mux Contentful app connects Contentful with your Mux account so that Mux can handle uploading and streaming of all videos.
This is a detailed guide for integrating the Contentful Mux app. To read more about the Mux app and why you may want to use it to power videos in your CMS, read [the announcement blog post on Contentful's blog](https://www.contentful.com/blog/improve-video-streaming-using-hls-with-mux-contentful/).

## 1. Enter Mux credentials

Create an access token in your Mux account - you will need both the access token ID and the access token secret in the Contentful application. The access token should have **Read** and **Write** permissions for Mux Video, and also **Read** for Mux Data.

<Image src="/docs/images/new-cms-token.png" width={608} height={480} alt="Mux Video access token permissions" sm />

## 2. Install App

In the Contentful dashboard click “Apps > Manage Apps” in the top navigation bar. Scroll down and find the Mux app, click it to start the installation flow.

Next you will see the configuration screen. You can come back to this screen after the app is installed to update the app configuration. Enter your Mux access token and Mux token secret. These are the same credentials you would use to make API requests yourself.

Assign Mux to JSON fields from your content model. In this example I am assigning Mux to take over the “Video Upload” field in my Blog post model. If you add new JSON fields later you can always come back to this configuration screen and assign Mux to the new fields.

<Image src="/docs/images/contentful-v110-install.png" width={1506} height={953} alt="Add API keys to integration." />

You can also assign Mux fields from the configuration of a particular Content model if you create a JSON Object type field and then edit its appearance as follows:

<Image src="/docs/images/contentful-v200-install-2.png" width={1506} height={953} alt="Edit field appearance to add Mux." />

### 2.1. Assign Mux Sidebar to a Content model

Once the plugin is installed and your Content Models are configured, you can add the Mux sidebar by clicking the plus sign and placing it wherever you want. Then click save. This sidebar is used to visualize pending changes with the publication of the Entry in Contentful. This is explained in more detail in the [Features and Important Notes](#7-mux-sidebar) section.

<Image src="/docs/images/contentful-v200-install-3.png" width={1506} height={953} alt="Add Mux sidebar to a Content model." />

## 3. Upload video

Create a new entry for your content model. You should see a drag and drop zone and file picker to select an audio or video file:

<Image src="/docs/images/contentful-v200-upload-1.png" width={1256} height={834} alt="Empty Video field" />

Select a video file and a modal will appear with expandable configuration options that you can click to expand and configure the video before it's uploaded. The configuration options available are:

* **Video Quality Settings:** Allows you to define the video quality. More information: [Use different video quality levels](/docs/guides/use-video-quality-levels)

* **Privacy Settings:** Allows you to define the video visibility between public or protected. More information: [Secure video playback](/docs/guides/secure-video-playback)

* **Metadata:** Allows you to add the title in the video metadata. This is also useful for having the title defined in the Mux Dashboard. More information: [Add metadata to your videos](/docs/guides/add-metadata-to-your-videos)

* **Captions:** Allows you to add captions to your videos, both custom and auto-generated captions.
  * To generate auto-generated captions, you need to specify the video language and it will automatically generate captions.
  * For custom captions, you need to specify the URL where the captions are located and the caption language.

More information in [Captions and Subtitles](#5-captions-and-subtitles) section.

* **MP4 Generation:** Allows you to generate static renditions for your videos, both audio-only and audio with video. More information: [Enable static MP4 renditions](/docs/guides/enable-static-mp4-renditions)

After configuring these options, click on the upload button and wait for the file to upload to Mux. The amount of time this takes will depend on your network upload speed and the size of the file. **Don't close the tab until the upload is complete.**

Additionally, entering a Mux Asset ID from an existing video in Mux, or a URL to an audio or video file will also work in the input field.

<Image src="/docs/images/contentful-v200-upload-2.png" width={1256} height={834} alt="Empty Video field" />

<Image src="/docs/images/contentful-v110-2.png" width={1256} height={833} alt="Uploading video." />

After the upload is complete you will see a message that says "Waiting for asset to be playable" while Mux is processing your video. For normal video files it should only take a minute or so, however longer files, or files that need more processing, may take longer.

<Image src="/docs/images/contentful-v110-3.png" width={1256} height={785} alt="Waiting for video to process." />

Your video is now playable via Mux. You will see a player with your video in the Contentful UI.

<Image src="/docs/images/contentful-v200-upload-3.png" width={1287} height={1295} alt="After a video is finished and ready to play." />

## 4. Playback

When you query your Mux video through the Contentful API you will get back a JSON object that looks something like this that is viewable in the Data tab:

```json
{
  "version": 3,
  "uploadId": "some-upload-id",
  "assetId": "some-asset-id",
  "playbackId": "YOUR_PLAYBACK_ID",
  "ready": true,
  "ratio": "16:9",
  "max_stored_resolution": "HD",
  "max_stored_frame_rate": 29.928,
  "duration": 23.857167,
  "audioOnly": false,
  "created_at": 1664219467,
  "audioTracks": [
    {
      "type": "audio",
      "primary": true,
      "max_channels": 2,
      "max_channel_layout": "stereo",
      "id": "some-audio-track-id",
      "duration": 10.026667
    }
  ],
  "meta": {
    "title": "some-video-title",
    "external_id": "some-external-id"
  }
}
```

You will need to pull out the `playbackId` property and construct a URL like this. You will use this URL with a player that supports HLS:

```text
https://stream.mux.com/{YOUR_PLAYBACK_ID}.m3u8
```

View Mux's [Playback docs](/docs/guides/play-your-videos) for more information about players.

## Using Mux Player

We made it easy to playback video using Mux Player by including the same code used to play the video in the Contentful dashboard. Simply head to the **Player Code** tab, click the copy button, and paste this into a website for quicker testing and development.
You can also add optional parameters to the example code such as autoplay, mute, and loop by clicking the corresponding checkboxes. This will update the example code for you to use.

Additionally, there's an option to get iframe example code for embedding the video, providing an alternative integration method.

<Image src="/docs/images/contentful-v200-player-code-1.png" width={1249} height={934} alt="Get the Mux Player code." />

## 5. Captions and Subtitles

Captions can be added before uploading an asset (see the [Upload video](#3-upload-video) section) and after uploading in the Captions tab. There are two ways to add captions: auto-generated and custom captions, and they can be used together if desired.

You must select the type of captions to upload from the dropdown menu.

<Image src="/docs/images/contentful-v200-captions-dropdown.png" width={1256} height={585} alt="Select caption type from dropdown" />

### Auto-Generated Captions

For auto-generated captions, you need to select the Language Code. This is automatically populated based on the Audio Name you select. It's important to select the same language as the spoken audio in the video so that captions are generated correctly.

The Audio Name is what will appear in the player when selecting the caption. You can use any name you want.

<Image src="/docs/images/contentful-v200-captions-auto.png" width={1256} height={585} alt="Auto-generated captions configuration" />

### Custom Captions

Custom captions and subtitles can come from any publicly available URL. Add the URL to the `vtt` or `srt` caption file, selecting the caption name and to mark as closed captions.

<Image src="/docs/images/contentful-v200-captions-custom.png" width={1256} height={585} alt="Adding custom captions/subtitles" />

One way to upload captions is to use the Contentful Media Manager. After uploading the file to the Manager, right click on the download button and select `copy link` then paste this link into the URL field.

<Image src="/docs/images/contentful-v110-cc-2.png" width={1447} height={1100} alt="Copying the file URL in Media Manager." />

### Managing Captions

Caption files can be added or deleted, and files can be downloaded for further editing. The stored JSON object will also reflect additional caption files. Existing captions will be displayed after clicking the `Resync` button under the Data tab or when entering to the entry. Deleting a caption will appear in the Mux sidebar and require publishing to take effect in Mux.

<Image src="/docs/images/contentful-v200-captions-result.png" width={1256} height={1151} alt="Captions or subtitles are working on video." />

## 6. Audio Tracks

You can add new audio tracks to an existing asset in the Audio Tracks section. This is useful for providing multiple language audio tracks or alternative audio content for your videos.

<Image src="/docs/images/contentful-v200-audio-tracks-tab.png" width={1256} height={585} alt="Audio Tracks tab" />

### Adding Audio Tracks

To upload a new audio track, you need to provide a public URL of an audio file. One way to obtain this is by using the Contentful Media Manager, in the same way as described in the [Captions section](#custom-captions).

You need to specify the Language Code, which is automatically populated when you indicate the Audio Name. The Audio Name can contain any text and is what will be displayed in the player when users want to select from the available audio tracks.

<Image src="/docs/images/contentful-v200-audio-tracks-upload.png" width={1256} height={585} alt="Adding new audio track" />

### Managing Audio Tracks

Audio tracks can be added or deleted, and the stored JSON object will reflect the additional audio tracks. Deleting an audio track will appear in the Mux sidebar and require publishing to take effect in Mux.

<Image src="/docs/images/contentful-v200-audio-tracks-result.png" width={1256} height={585} alt="Audio tracks in player" />

## 7. Mux Sidebar

The Mux sidebar provides a visual interface to track the synchronization status between your Contentful entries and Mux assets. This sidebar displays:

* Pending actions that need to be synchronized with Mux
* Any changes that require publishing to take effect in Mux

<Image src="/docs/images/contentful-v200-feature-sidebar-1.png" width={350} height={953} alt="Mux sidebar." sm />

<Image src="/docs/images/contentful-v200-feature-sidebar-2.png" width={350} height={953} alt="Mux sidebar." sm />

## 8. Publishing Requirements and Breaking Changes

As part of the [version 2.0 plugin release](https://github.com/contentful/apps/pull/9826), changes were made to maintain data integrity and consistency between what is published in Contentful and what is stored in Mux. For example, to change a video from public to protected, its playback ID must be regenerated, and if that video is published in Contentful, the new playback ID couldn't be obtained previously.

To solve this, some actions that we consider "breaking changes" will not be executed in Mux until the user clicks "Publish" on the pending changes.

### Breaking Changes That Require Publishing

The following changes will appear as pending in the Mux sidebar and will only be applied to Mux when you click "Publish changes" in Contentful:

* **Delete Video** - Removing a video asset
* **Delete Captions** - Removing caption/subtitle files
* **Delete Audio** - Removing audio tracks
* **Change Metadata Title** - Modifying the title in the metadata section
* **Delete MP4 Renditions** - Removing static MP4 files
* **Change Video Visibility** - Switching between public/protected settings

<Image src="/docs/images/contentful-v200-pending-changes-sidebar.png" width={1506} height={953} alt="Mux sidebar showing pending changes" />

### Publishing Workflow

For example, if you want to change the visibility of an existing video, this will be a pending change that appears in the sidebar and the change will be made in Mux when you click "Publish changes" in Contentful.

<Image src="/docs/images/contentful-v200-publish-changes-process.png" width={1506} height={953} alt="Publishing changes process" />

The same happens with actions like deletions - if you want to delete a caption, it will appear marked for deletion but will only be removed when you publish. You can also undo deletions to prevent them from being deleted when you publish.

## Explore advanced options

## Advanced: Signed URLs

<Callout type="warning" title="Warning! Requires generating JWT on your server">
  Enabling signed URLs in Contentful will require you to generate your own signing tokens on your application **server**. This involves creating a signing key and using that to generate JSON web tokens when you want to access your videos and thumbnails outside of Contentful.
</Callout>

By default, all assets uploaded to Mux through Contentful will be created with a single playback policy of `"public"`. This means that your videos and thumbnails are accessible with `https://stream.mux.com/{PLAYBACK_ID}.m3u8` and `https://image.mux.com/{PLAYBACK_ID}/thumbnail.jpg`.

If you want more control over controlling the playback and thumbnail access, you can enable this feature on the Contentful configuration page:

<Image src="/docs/images/contentful-v110-config.png" width={1505} height={1158} alt="Additional global configuration options." />

When you enable this feature, the following things will happen:

1. The Mux App in Contentful will use the Mux API to create a URL signing key and save this with your Contentful configuration.
2. When uploading an asset, you can select "Protected" in the Privacy Settings section of the configuration modal. If you select this option, the asset will be created with `playback_policy: "signed"` (instead of `"public"`).
3. The signing key from Step 1 will be used by the Mux App to preview content inside the Contentful UI.
4. When you access your content in your own application, outside of Contentful, the Mux asset will no longer have the key `playbackId`, it will now be called `signedPlaybackId`.

```json
{
  "uploadId": "some-upload-id",
  "assetId": "some-asset-id",
  "signedPlaybackId": "YOUR_SIGNED_PLAYBACK_ID",
  "ready": true,
  "ratio": "16:9"
}
```

5. You should use the value from `signedPlaybackId` to create URLs for playback and for thumbnail generation.

* Playback `https://stream.mux.com/{SIGNED_PLAYBACK_ID}.m3u8?token={TOKEN}`
* Thumbnails `https://image.mux.com/{SIGNED_PLAYBACK_ID}/thumbnail.jpg?token={TOKEN}`

6. The `TOKEN` parameter for the above URLs is something you create on your server according to Step 2 in [Security: Signed URLs](/docs/guides/secure-video-playback).

Note that in the Contentful UI when an asset is using a signed URL you will see this notice.

<Image src="/docs/images/contentful-v110-notice.png" width={1444} height={573} alt="Signed Notice" />

Public and signed playback IDs can be toggled per-entry under the Data tab. Each time the IDs are toggled, the old playback ID is deleted, and a new ID is created in its place.

## Advanced: DRM

DRM (Digital Rights Management) provides the highest level of content protection using industry-standard encryption. To use DRM, you must first [request access](https://www.mux.com/support/human). Once approved, you'll receive a DRM Configuration ID.

[Learn more about DRM](/docs/guides/protect-videos-with-drm).

### Enabling DRM in Contentful

To enable DRM support, go to the Contentful Mux app configuration screen. You will see a new **"Advanced: DRM"** section where you can:

1. Enable or disable DRM support by checking the **Enable DRM** checkbox
2. Enter your **DRM Configuration ID** provided by Mux after your DRM access is approved

<Image src="/docs/images/contentful-drm-1.png" width={1444} height={573} alt="Enable DRM in Contentful" />

This configuration is stored alongside your other installation parameters and applies to all videos uploaded through this Contentful space.

### Uploading DRM-protected videos

Once DRM is enabled and configured, a new **"DRM Protected"** option will appear in the **Privacy Settings** section when uploading or editing a video asset.

* The DRM option is only available when both DRM is enabled **and** a valid DRM Configuration ID is present
* If DRM is enabled but no Configuration ID is set, the option will appear disabled with contextual help text
* When DRM is enabled and configured, **DRM Protected** is selected by default (users can change it to Public or Protected if needed)

<Image src="/docs/images/contentful-drm-2.png" width={1444} height={573} alt="DRM Protected option in Privacy Settings" />

**Note:** DRM is only available for video assets. If you attempt to upload an audio-only file with DRM selected, the app will display a warning indicating that DRM is not supported for audio files. Use the **Protected** option for secure playback of audio content.

### Viewing DRM-protected videos

To view DRM-protected videos in Contentful, you must also have **Signed URLs enabled** in the app configuration. If Signed URLs are not enabled, you will see an error message when trying to view DRM content. See [Advanced: Signed URLs](#advanced-signed-urls) for setup instructions.

Due to browser iframe security restrictions, **DRM-protected videos cannot be played directly within the Contentful preview**. When viewing a DRM-protected asset, you will see a notice explaining this limitation.

The app provides an **"Open in external player"** link that opens the video in a standalone browser tab where DRM playback is permitted. This external player uses a Blob URL to load the player with the required DRM tokens.

<Image src="/docs/images/contentful-drm-3.png" width={1444} height={573} alt="DRM warning in preview" />

**Note:** DRM playback incurs additional costs. See the [DRM pricing](/docs/guides/protect-videos-with-drm#pricing) section for details.

### Using DRM-protected videos in your application

When you query a DRM-protected video through the Contentful API, the JSON object will include DRM-related information. The generated player code in the **Player Code** tab will automatically include the necessary DRM tokens (`playback`, `thumbnail`, `storyboard`, and `drm` tokens), so you can copy and paste it directly into your application.

For more detailed information about implementing DRM playback, including token generation and player configuration, see the [DRM guide](/docs/guides/protect-videos-with-drm).

## Note about version 2.0 release

During the month of August 2025, [version 2.0 of the plugin was released](https://github.com/contentful/apps/pull/9826). No action is required as the plugin updates automatically.

There are no changes to the previous data structure, but new fields have been added that are necessary to support new features such as audio tracks, MP4 renditions, and others.

If you were already using the plugin previously, you may notice that when entering an entry that has a video, it changes to 'Changed' status. This occurs because the new video data is retrieved and stored in Contentful's data. It's like an automatic resync that runs.

This is expected behavior and only occurs with videos uploaded before the new version or if changes are made to videos outside of Contentful, as it synchronizes automatically.

## Note about migrating from the old Contentful extension

Before releasing the Contentful App, Mux had an officially supported [Contentful extension](https://github.com/contentful/extensions/pull/316).

The underlying data structure has not changed, so you can safely migrate to the app without losing data by following these steps:

1. Uninstall the extension (now your video fields should look like raw JSON)
2. Install the app
3. On the configuration screen, apply the Mux App to the video fields that you had before

<GuideCard title="Set up playback" description="Set up your iOS application, Android application or web application to start playing your Mux assets" links={[{ title: 'Read the guide', href: '/docs/guides/play-your-videos' }]} />

<GuideCard title="Preview your video" description="Now that you have Mux assets, build rich experiences into your application by extracting images from your videos" links={[{ title: 'Read the guide', href: '/docs/guides/get-images-from-a-video' }]} />

<GuideCard title="Integrate Mux Data" description="Add the Mux Data SDK to your player and start collecting playback performance metrics." links={[{ title: 'Read the guide', href: '/docs/guides/track-your-video-performance' }]} />


# Integrate with WordPress
Learn how to integration Mux with WordPress. The Mux Video Uploader by 2Coders plugin connects WordPress with your Mux account so you can upload, manage and embed videos on your site or application from your WordPress account.
This guide explains how to integrate Mux with WordPress using the Mux Video Uploader plugin by 2Coders. This integration enables you to leverage Mux's powerful video infrastructure while maintaining the familiar WordPress content management experience.

Follow the steps below to integrate Mux with your WordPress site

## 1. Install the Plugin

WordPress plugin can be installed either from WordPress Plugin Directory or Manually by uploading a zipped plugin file.

<Callout type="info">
  You should be on WordPress.com Business pricing plan or higher to install the Mux Video Uploader plugin. However, there is no such requirement for Self-Hosted WordPress instance.
</Callout>

### A. From the WordPress Plugin Directory

1. In your WordPress admin panel, navigate to `Plugins > Add Plugin` on the sidebar

2. Search and select "Mux Video Uploader by 2Coders"

   <Image src="/docs/images/wordpress-plugin-search.jpg" width={2404} height={526} />

3. Click `Install and activate` button on the plugin page.

   <Image src="/docs/images/wordpress-plugin-install.jpg" width={2404} height={526} />

### B: Manual Installation

1. Download the plugin ZIP file from the [WordPress.org site](https://downloads.wordpress.org/plugin/2coders-integration-mux-video.latest-stable.zip).

2. In your WordPress admin panel, go to `Plugins > Add Plugin`

3. Click Upload Plugin and select the downloaded ZIP file

   <Image src="/docs/images/wordpress-plugin-upload.jpg" width={2404} height={526} />

4. Click Install Now and then Activate Plugin

After the plugin is properly installed, you should see the Mux Video Uploader plugin on the `Installed Plugins` page.

<Image src="/docs/images/wordpress-plugin-installed.jpg" width={2404} height={526} />

## 2. Create a Mux Account

If you don't already have a Mux account:

1. Sign up at [mux.com](https://dashboard.mux.com/signup)
2. After creating your account, navigate to your dashboard
3. Generate [API Access Token](https://www.mux.com/docs/core/stream-video-files#1-get-an-api-access-token). You'll need both an Access Token ID and Secret Key for the plugin to make API requests.

   <Image src="/docs/images/settings-api-access-tokens.png" width={500} height={500} />

   The access token should have Mux Video Read and Write permissions as well as Mux Data (read-only).

   <Image src="/docs/images/new-cms-token.png" width={1102} height={468} alt="Mux Video and Mux Data access token permissions" />

## 3. Connect WordPress to Mux

In your WordPress admin panel, locate the new Mux Video menu item

1. Go to Mux Video > Settings
2. Enter your Mux API credentials (API ID and Secret Key)
3. Click Save Settings

   <Image src="/docs/images/wordpress-plugin-token.jpg" width={2404} height={526} />

## 4. Upload Video

The video below shows how to upload a video on WordPress using the Mux Video Uploader plugin. You can also enable advanced features, like Signed URLs, Subtitles & Captions, and MP4 generation during asset creation or later on from the `Assets List` page.

<Player playbackId="K1UOWbEJzRTNNiwAVywTS5CBuZD02iSIM" muted loop thumbnailTime={0} title="How to upload a video on Wordpress using Mux Video Uploader plugin" />

## 5. Play Video

### Using the Gutenberg Block

The uploaded video can be added to your WordPress site using the Gutenberg block Editor:

1. Add or Edit a post or page
2. Click the + icon to add a new block
3. Search for "Mux Video" and select the block
4. Choose the asset from the list and click `Insert`
5. Preview and publish your content

When using the Gutenberg block, the video is embedded onto the page with the default Mux Player configuration.

<Player playbackId="ita1G6KmqTIOlQJ017m6mDJwJver31Cwi" muted loop thumbnailTime={0} title="Embed Video with Gutenberg block using Mux Video Uploader WordPress plugin" />

### Using the Shortcode Block

The same uploaded videos can also be added using the Shortcode block. With the Shortcode, you can customize the Mux Player configuration instead of using the default configuration.

<Player playbackId="aI1gVG4LX01bmbh5USPdm5OTGsqgBoJsz" muted loop thumbnailTime={0} title="Embed Video with Shortcode block using Mux Video Uploader WordPress plugin" />

## Advanced video options

### Video Quality Levels

When uploading a new video, you can select which Video Quality Levels is used when preparing the Asset. Possible selections are `Basic`, `Plus` and `Premium`. More details can be found in our [Use Video Quality Options](/docs/guides/use-video-quality-levels) guide.

### MP4 Generation

Each Asset can be enabled to generate downloadable MP4s. You can select `Highest` or `Audio-Only` or both. This will create [Static Renditions](/docs/guides/enable-static-mp4-renditions) for the Asset and will make MP4 files available for download to client devices using a formatted URL.

### Signed Tokens

When uploading a new video, you can select `Protected` option when you want to secure the video playback and `Public` to make the video publicly available. Learn more about [Secure video playback](/docs/guides/secure-video-playback).

<Image src="/docs/images/wordpress-plugin-signed-url.jpg" width={2404} height={526} />

Mux Video Uploader plugin creates a signing key when configuring the Access Tokens on the plugin's Settings page. The plugin generates Signed URLs when `Protected` option is selected when uploading the video and available on the Asset page as shown in the image below.

<Image src="/docs/images/wordpress-plugin-view-asset-signed-url.jpg" width={2404} height={526} />

### Auto-Generate and Upload Custom Captions/Subtitles

With Mux's auto-generated captions, you can easily add captions to videos uploaded by selecting the language of the spoken words. Mux can generate captions automatically while preparing the asset or later. For generating captions later, go to that Asset entry on the Asset List section of the plugin and click on `Add Captions`. More details can be found in the [Add auto-generated captions to your videos and use transcripts](/guides/add-autogenerated-captions-and-use-transcripts) section of our documentation.

<Callout type="warning" title="Warning! Auto-generate a single caption track">
  The "Auto-generated" captions configuration should only be used to generate a single language captions track. The language selected must match the spoken language.
</Callout>

Additionally, you can upload one or more custom caption files (during asset creation step or later) for a single asset.


# Integrate with Strapi
Strapi is an open source content management system that allows you to define your own schemas for your content.
The [Mux Video Uploader](https://www.npmjs.com/package/strapi-plugin-mux-video-uploader) plugin allows editors to upload content directly to Mux from within the Strapi interface, then associate those videos with their custom collection types.

## Requirements

* A working installation of Strapi that is publicly accessible through a hostname
* An [Access Token and Secret Key](/docs/integrations/strapi#2-create-access-in-mux) which is provisioned within Mux Dashboard
* Configure a [Webhooks listener](/docs/integrations/strapi#3-configure-webhook-listener) within Mux Dashboard so that Strapi can be informed of upload progress.

## 1. Install the Mux Video Uploader plugin

With your existing Strapi installation, run the following command in the root of your Strapi project to install the plugin. Be sure to restart Strapi for the plugin to take effect.

<Callout type="info" title="Version notice">
  As of the 2.1.0 version of this player, only Strapi v4 will be supported. To use with Strapi v3, please use version 2.0.0 of this plugin.
</Callout>

## Install instructions for Strapi v5

Run this command in your project folder if you are using NPM:

```sh
npm i strapi-plugin-mux-video-uploader@latest
```

Or this command if you are using yarn with your project:

```sh
yarn add strapi-plugin-mux-video-uploader@latest
```

## Install instructions for Strapi v4

Run this command in your project folder if you are using NPM:

```sh
npm i strapi-plugin-mux-video-uploader@2.8.4
```

Or this command if you are using yarn with your project:

```sh
yarn add strapi-plugin-mux-video-uploader@2.8.4
```

## Install instructions for Strapi v3

Run this command in your project folder if you are using NPM:

```sh
npm i strapi-plugin-mux-video-uploader@2.0.0
```

Or this command if you are using yarn with your project:

```sh
yarn add strapi-plugin-mux-video-uploader@2.0.0
```

## 2. Create access token in Mux

Generate a new Access Token by going to the Access Token settings of your Mux account dashboard.

<Image src="/docs/images/settings-api-access-tokens.png" width={500} height={500} alt="Mux access token settings" />

The access token should have Mux Video **Read** and **Write** permissions.

<Image src="/docs/images/new-access-token.png" width={760} height={376} alt="Mux Video access token permissions" sm />

After clicking the "Generate Token" button, save the "Access Token ID" and "Secret Key" to be used later.

## 3. Configure Webhook listener

Part of the upload process includes Mux updating Strapi with the completion of the upload and processing. In order for Mux to make this communication, a Webhook needs to be established so that events are sent to your Strapi installation.

Create a new Webhook configuration in Mux Dashboard. There will be a space to add a "URL to notify". This value should be formatted based on your Strapi's hostname

```txt
{YOUR_STRAPI_DOMAIN_HERE}/mux-video-uploader/webhook-handler
```

After saving, copy the "Signing Secret" which will be used later.

## 4. Setup configuration in Strapi

In Strapi, visit the Settings page and navigate to the `MUX VIDEO UPLOADER` section.

Using the details saved in the previous step, fill in the fields with the appropriate values.

<Image src="/docs/images/strapi-config.png" width={1600} height={819} />

Click the "Save" button to persist the changes.

## 5. Upload video

Use the Mux Video Uploader page that is now available in Strapi's menu to upload either with a remote URL or directly using a local video file.

<Image src="/docs/images/strapi-upload.png" width={1600} height={819} />

From here, relationships of Mux assets can be modeled to custom collection types within Strapi to tie metadata with playable content.

<Player playbackId="kBwjf6dl6028FjELH4p0297dLcPM6AvQ1D" thumbnailTime="0" title="Strapi - Mux Video Uploader - Upload" />

<Callout type="success" title="Congratulations!">
  You now have the ability to upload content to Mux through Strapi CMS!
</Callout>

At this point, querying Strapi using REST or GraphQL will give you access to the `playback_id` information. This `playback_id` can be used by your client applications to stream content or retrieve thumbnails.

## 6. Explore advanced options

## Signed tokens

<Callout type="warning" title="Warning! Requires generating JWT on your server">
  Enabling signed URLs in Strapi will require you to generate your own signing tokens on your application **server**. This involves creating a signing key and using that to generate JSON web tokens when you want to access your videos and thumbnails outside of Strapi.
</Callout>

By default, all assets uploaded to Mux through Strapi will be created with a playback policy of `"public"`. This means that your videos and thumbnails are accessible with `https://stream.mux.com/{PLAYBACK_ID}.m3u8` and `https://image.mux.com/{PLAYBACK_ID}/thumbnail.jpg`.

If you want more control over delivery of the playback and thumbnail access, you can enable this feature in the Strapi settings for the Mux Video Uploader.

When you enable this feature, the following things will happen:

1. The Mux Plugin in Strapi will save the signing keys that you've generated and be available immediately.
2. Any Assets that get created with the Signed Playback URL setting enabled will be created with `playback_policy: "signed"` (instead of `"public"`).
3. The signing key from Step 1 will be used by the Mux Plugin to preview content inside the Strapi UI.
4. When you access your content in your own application, use the `MuxAsset.signed` property to determine if the asset is signed by either a `true` or `false` value.

```json
{
  "id": 9,
  "upload_id": null,
  "asset_id": "H9H01yni83yRLuu6cKaf8jQI8XW01SPp5XI7WrGsD37n00",
  "playback_id": "aAqXNee00zlfzR2Rsw01NmGBvxSg1Ocs3g008YChvtG6aM",
  "signed": true,
  "isReady": true,
  "duration": 25.492133,
  "aspect_ratio": "16:9",
  "createdAt": "2024-04-01T23:48:19.760Z",
  "updatedAt": "2024-04-01T23:48:21.605Z"
}
```

5. You should use the signed `playback_id` to create URLs for playback and for thumbnail generation.

* Playback `https://stream.mux.com/{SIGNED_PLAYBACK_ID}.m3u8?token={TOKEN}`
* Thumbnails `https://image.mux.com/{SIGNED_PLAYBACK_ID}/thumbnail.jpg?token={TOKEN}`

6. The `TOKEN` parameter for the above URLs is something you create on your server according to Step 2 in [Secure video playback](/docs/guides/secure-video-playback)

Note that in the Strapi UI when an asset is using a signed URL you will see a lock icon on the Asset list.

## Encoding Tiers

When uploading a new video, you can select what Encoding Tier used when preparing the Asset.  Possible selections are `Smart` and `Baseline`.  When choosing `Smart` additional options are made available for maximum stream resolutions (1080p, 2K or 4K).

More details can be found in our [Use Encoding Tiers](/docs/guides/use-video-quality-levels) section.

## Static Renditions

When using the `Smart` Encoding Tier, an option to enable downloadable MP4s will be available.  This option will create [Static Renditions](/docs/guides/enable-static-mp4-renditions) for the Asset and will make MP4 files available for download to client devices using a formatted URL.

## Captions/Subtitles

With Mux's auto-generated captions, editors can easily add captions to videos being uploaded from Strapi.  When using the "Auto-generated" option, Mux will generate captions automatically while it prepares the Asset.  More details can be found in the [Add auto-generated captions to your videos and use transcripts](/docs/guides/add-autogenerated-captions-and-use-transcripts) section of our documentation.

If you choose to upload a "Custom" captions file (supported formats are `.vtt` and `.srt`), your file will be uploaded to your instance of Strapi and Mux will pull it via a public URL from your Strapi instance.  Take a look at our [Add subtitles/captions to videos](/docs/guides/add-subtitles-to-your-videos) for more details.

<GuideCard title="Set up playback" description="Set up your iOS application, Android application or web application to start playing your Mux assets" links={[{ title: 'Read the guide', href: '/docs/guides/play-your-videos' }]} />

<GuideCard title="Preview your video" description="Now that you have Mux assets, build rich experiences into your application by extracting images from your videos" links={[{ title: 'Read the guide', href: '/docs/guides/get-images-from-a-video' }]} />

<GuideCard title="Integrate Mux Data" description="Add the Mux Data SDK to your player and start collecting playback performance metrics." links={[{ title: 'Read the guide', href: '/docs/guides/track-your-video-performance' }]} />


# Integrate with Cosmic
With the Mux Video integration for Cosmic JS, you can upload videos directly to Mux from your Cosmic JS Dashboard.
## 1. Install the Mux Extension

Log in to your Cosmic JS account and navigate to Your Bucket > Settings > Extensions. Click the Extensions tab and find the Mux Videos Extension. Hit Install.

<Image src="/docs/images/cosmic-install.png" width={1304} height={828} />

## 2. Enter Mux credentials

After installing, you will be redirected to the Extension settings page. Under Query Parameters, you will need to provide the Mux API credentials on your Mux account (mux\_access\_token, mux\_secret).

<Image src="/docs/images/cosmic-credentials.png" width={1304} height={828} />

If you need to generate a new Access Token, go to the Access Token settings of your Mux account dashboard.

<Image src="/docs/images/settings-api-access-tokens.png" width={500} height={500} />

The access token should have **Read** and **Write** permissions for Mux Video.

<Image src="/docs/images/new-access-token.png" width={760} height={376} alt="Mux Video access token permissions" sm />

Go back to the Cosmic Extensions setting page, enter your Mux credentials, and save your Extension.

## 3. Upload video

After installing the Extension and setting your Mux account keys, click the Mux Videos Extension link in the left-hand nav. Next, upload your videos.

<Image src="/docs/images/cosmic-upload.gif" width={1273} height={781} />

The Extension saves the uploaded video data to the Mux Videos Object Type. Now you can add your Mux Videos to any Object using an Object metafield. Then you can fetch Mux data into your application by using the `mux_playback_url` property located in the Object metadata.

<Image src="/docs/images/cosmic-edit.gif" width={1272} height={827} />

## 4. Playback

To retrieve your video for playback, check out the [Cosmic docs](https://www.cosmicjs.com/articles/mux-videos-extension-overview-jqpvec5d) to see how to add the Mux playback URL to your HTML Video player.

<GuideCard title="Set up playback" description="Set up your iOS application, Android application or web application to start playing your Mux assets" links={[{ title: 'Read the guide', href: '/docs/guides/play-your-videos' }]} />

<GuideCard title="Preview your video" description="Now that you have Mux assets, build rich experiences into your application by extracting images from your videos" links={[{ title: 'Read the guide', href: '/docs/guides/get-images-from-a-video' }]} />

<GuideCard title="Integrate Mux Data" description="Add the Mux Data SDK to your player and start collecting playback performance metrics." links={[{ title: 'Read the guide', href: '/docs/guides/track-your-video-performance' }]} />


# Integrate with DatoCMS
With every DatoCMS project you also get a native integration with Mux without any manual intervention.
Mux is by default enabled in every new DatoCMS project. The integration allows you to upload videos directly from DatoCMS dashboard or using the [REST API](https://www.datocms.com/docs/content-management-api). The CMS interface will then allow you to use the videos in the content, while on the API side you’ll be able to retrieve the Mux Video URLs and the thumbnail.

## 1. Upload video

Just drag and drop a video in DatoCMS media area, like this:

<Image src="/docs/images/datocms-upload.gif" width={640} height={360} />

## 2. Fetch video information via GraphQL

For every video that you upload, you can get on the API a custom video object with the following properties:

* HLS video streaming URL.
* High, medium and low quality MP4 versions of the video to support legacy browsers that do not support HLS.
* Duration and frame rate of the video.
* Thumbnail URL: resizable, cropable and available in JPEG, PNG and GIF format.

See the full page of this embedded example [here in the GraphQL explorer](https://cda-explorer.datocms.com/?embed=\&apitoken=faeb9172e232a75339242faafb9e56de8c8f13b735f7090964\&query=%7B%0A%20%20allUploads%28filter%3A%20%7Btype%3A%20%7Beq%3A%20video%7D%2C%20resolution%3A%20%7B%7D%2C%20smartTags%3A%20%7B%7D%7D%29%20%7B%0A%20%20%20%20video%20%7B%0A%20%20%20%20%20%20streamingUrl%0A%20%20%20%20%20%20mp4High%3A%20mp4Url%28res%3A%20high%29%0A%20%20%20%20%20%20mp4Med%3A%20mp4Url%28res%3A%20medium%29%0A%20%20%20%20%20%20mp4Low%3A%20mp4Url%28res%3A%20low%29%0A%20%20%20%20%20%20duration%0A%20%20%20%20%20%20framerate%0A%20%20%20%20%20%20thumbJpg%3A%20thumbnailUrl%28format%3A%20jpg%29%0A%20%20%20%20%20%20thumbPng%3A%20thumbnailUrl%28format%3A%20png%29%0A%20%20%20%20%20%20thumbGif%3A%20thumbnailUrl%28format%3A%20gif%29%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%0A).

<iframe src="https://cda-explorer.datocms.com/?embed=&apitoken=faeb9172e232a75339242faafb9e56de8c8f13b735f7090964&query=%7B%0A%20%20allUploads%28filter%3A%20%7Btype%3A%20%7Beq%3A%20video%7D%2C%20resolution%3A%20%7B%7D%2C%20smartTags%3A%20%7B%7D%7D%29%20%7B%0A%20%20%20%20video%20%7B%0A%20%20%20%20%20%20streamingUrl%0A%20%20%20%20%20%20mp4High%3A%20mp4Url%28res%3A%20high%29%0A%20%20%20%20%20%20mp4Med%3A%20mp4Url%28res%3A%20medium%29%0A%20%20%20%20%20%20mp4Low%3A%20mp4Url%28res%3A%20low%29%0A%20%20%20%20%20%20duration%0A%20%20%20%20%20%20framerate%0A%20%20%20%20%20%20thumbJpg%3A%20thumbnailUrl%28format%3A%20jpg%29%0A%20%20%20%20%20%20thumbPng%3A%20thumbnailUrl%28format%3A%20png%29%0A%20%20%20%20%20%20thumbGif%3A%20thumbnailUrl%28format%3A%20gif%29%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%0A" title="CDA GraphQL Explorer | DatoCMS" width="100%" height="500px" style={{ border: "none" }} />

<GuideCard title="Set up playback" description="Set up your iOS application, Android application or web application to start playing your Mux assets" links={[{ title: 'Read the guide', href: '/docs/guides/play-your-videos' }]} />

<GuideCard title="Preview your video" description="Now that you have Mux assets, build rich experiences into your application by extracting images from your videos" links={[{ title: 'Read the guide', href: '/docs/guides/get-images-from-a-video' }]} />

<GuideCard title="Integrate Mux Data" description="Add the Mux Data SDK to your player and start collecting playback performance metrics." links={[{ title: 'Read the guide', href: '/docs/guides/track-your-video-performance' }]} />


# Integrate with Prepr
Prepr is a headless content management system created for adaptive websites. Prepr works with Mux out of the box. No configuration needed.
Mux is enabled for every new Prepr account by default. You can upload your videos to Prepr, add them to a content model and query their URLs to display them on your website. Follow the steps below to get started.

# Using Mux with Prepr

## Upload video content to Prepr

1. [Create a free Prepr account](https://signup.prepr.io/) before you get started.
2. Log in to the Prepr and navigate to the *Media* page.
3. Simply drag and drop audio/video files from your local folders into the *Media* page directly.

<Image src="/docs/images/prepr-demo-1.png" width={800} height={450} />

Once uploaded, the videos are ready to be used in content items.

## Add live streams to Prepr

1. Navigate to the *Media* page.
2. Click the *Upload asset* dropdown and choose the *Add live stream* option.

<Image src="/docs/images/prepr-demo-2.png" width={800} height={450} />

3. Enter the broadcasting details as described in the [Start broadcasting a live stream](/docs/guides/start-live-streaming#3-start-broadcasting) guide.

The livestream asset is now ready to be used in content items with an asset field.

## Add videos to content items

Once your video(s) have been uploaded, you can add them to a content item. Follow the steps below to do this.

1. Navigate to the *Content* page.
2. Create a new content item or open one of your existing content items with an assets field.
3. Simply drag and drop audio/video files from your local folders into the field directly or click the assets field to add the video you previously uploaded to the *Media* page.
4. Save or publish the content item to make the video available to the front-end application.

<Image src="/docs/images/prepr-demo-3.gif" width={800} height={450} />

## Querying the GraphQL API

Now you can query the URLs or playback IDs of your videos to embed them on your website.

To learn how to play video content on your website, please follow these instructions provided by Mux.

Your query could look something like the example below. In this example, `Posts` is the name of your content model and `videos` is the name of the assets field. It has various options:

* The HLS streaming URL is returned by default as the `url` field.
* The playback ID can be returned by using the `playback_id` field.
* You can use the `res` option to request MP4 versions in high, medium and/or low quality to support legacy browsers that do not support HLS.
* You can query the duration of video content using the `duration` option.
* The cover image can be requested using the `cover` field. It is adjustable using width, height, animated, and time arguments.

```gql
{
  Posts {
    items {
      videos {
        hls: url
        playback_id
        mp4High: url(res: "high")
        mp4Medium: url(res: "medium")
        mp4Low: url(res: "low")
        duration
        cover
      }
    }
  }
}
```

# Using additional Mux features in Prepr

## Static Renditions

By default Prepr uses the [plus quality level](/docs/guides/use-video-quality-levels), MP4 support is enabled on all accounts. This option will create Static Renditions for the Asset and will make MP4 files available for download to client devices using the `url` field.

## Captions/Subtitles

While editing an asset from the *Media* page, content editors can easily upload their own captions file (supported formats are .vtt and .srt) by clicking the **+ Add subtitles** link. Take a look at [Add subtitles/captions to videos](/docs/guides/add-subtitles-to-your-videos) for more details.

<GuideCard title="Set up playback" description="Set up your iOS application, Android application or web application to start playing your Mux assets" links={[{ title: 'Read the guide', href: '/docs/guides/play-your-videos' }]} />

<GuideCard title="Preview your video" description="Now that you have Mux assets, build rich experiences into your application by extracting images from your videos" links={[{ title: 'Read the guide', href: '/docs/guides/get-images-from-a-video' }]} />

<GuideCard title="Integrate Mux Data" description="Add the Mux Data SDK to your player and start collecting playback performance metrics." links={[{ title: 'Read the guide', href: '/docs/guides/track-your-video-performance' }]} />


# Database schema design
Recommended database schemas for storing Mux video data in your application. Copy-paste schemas for PostgreSQL, MySQL, and MongoDB.
When you build a video application with Mux, the video infrastructure (encoding, storage, and delivery) is managed for you. Your application's database stores references to Mux resources (assets, uploads, webhook events) along with your own metadata like titles, user ownership, and visibility.

This guide provides recommended schemas you can copy directly into your database.

## What to store

Most Mux integrations need these tables:

| Table | Purpose |
|---|---|
| **mux\_assets** | Video assets synced from Mux, including status, playback IDs, duration, and resolution |
| **mux\_uploads** | Direct upload records that track upload status and the resulting asset ID |
| **mux\_webhook\_events** | Webhook event log for debugging and ensuring reliable event processing |
| **videos** (optional) | Your app-level metadata like title, description, user ownership, visibility, and tags |

The first three tables mirror data from the Mux API, kept in sync via [webhooks](/docs/core/listen-for-webhooks). The `videos` table is your application's layer on top, linking Mux assets to your users and adding metadata that Mux doesn't manage.

<Callout type="info">
  If you're using [Supabase](/docs/integrations/supabase) or [Convex](/docs/integrations/convex), these tables are created for you automatically. This guide is for teams managing their own database schema.
</Callout>

## Recommended schema

### Assets

The `mux_assets` table stores video asset data synced from Mux. This is the most important table because it tracks every video in your system.

```postgresql
CREATE TABLE mux_assets (
mux_asset_id TEXT PRIMARY KEY,
status TEXT NOT NULL DEFAULT 'preparing',
playback_ids JSONB DEFAULT '[]',
duration NUMERIC,
aspect_ratio TEXT,
resolution_tier TEXT,
max_stored_frame_rate NUMERIC,
video_quality TEXT,
upload_id TEXT,
live_stream_id TEXT,
passthrough TEXT,
tracks JSONB DEFAULT '[]',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX idx_mux_assets_status ON mux_assets (status);
CREATE INDEX idx_mux_assets_upload_id ON mux_assets (upload_id);
CREATE INDEX idx_mux_assets_created_at ON mux_assets (created_at);
```

```mysql
CREATE TABLE mux_assets (
mux_asset_id VARCHAR(255) PRIMARY KEY,
status VARCHAR(50) NOT NULL DEFAULT 'preparing',
playback_ids JSON DEFAULT ('[]'),
duration DECIMAL(12, 4),
aspect_ratio VARCHAR(20),
resolution_tier VARCHAR(20),
max_stored_frame_rate DECIMAL(8, 4),
video_quality VARCHAR(20),
upload_id VARCHAR(255),
live_stream_id VARCHAR(255),
passthrough VARCHAR(255),
tracks JSON DEFAULT ('[]'),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

CREATE INDEX idx_mux_assets_status ON mux_assets (status);
CREATE INDEX idx_mux_assets_upload_id ON mux_assets (upload_id);
CREATE INDEX idx_mux_assets_created_at ON mux_assets (created_at);
```

```mongodb
db.createCollection("mux_assets");

db.mux_assets.createIndex({ mux_asset_id: 1 }, { unique: true });
db.mux_assets.createIndex({ status: 1 });
db.mux_assets.createIndex({ upload_id: 1 });
db.mux_assets.createIndex({ created_at: -1 });

// Example document structure:
// {
//   mux_asset_id: "abc123",
//   status: "ready",
//   playback_ids: [{ id: "playback123", policy: "public" }],
//   duration: 120.5,
//   aspect_ratio: "16:9",
//   resolution_tier: "1080p",
//   max_stored_frame_rate: 29.97,
//   video_quality: "plus",
//   upload_id: "upload123",
//   live_stream_id: null,
//   passthrough: "my-custom-id",
//   tracks: [
//     { id: "track1", type: "video", max_width: 1920, max_height: 1080 },
//     { id: "track2", type: "audio", max_channels: 2 }
//   ],
//   created_at: ISODate("2026-01-15T10:30:00Z"),
//   updated_at: ISODate("2026-01-15T10:35:00Z")
// }
```



#### Key fields

| Field | Description |
|---|---|
| `mux_asset_id` | The unique identifier from Mux. Use this as your primary key or unique constraint. |
| `status` | Asset lifecycle state: `preparing`, `ready`, or `errored`. Only serve videos with status `ready`. |
| `playback_ids` | Array of `{ id, policy }` objects. The `id` is used to construct playback URLs. `policy` is `public`, `signed`, or `drm`. |
| `duration` | Video length in seconds. |
| `aspect_ratio` | Format string like `16:9` or `4:3`. Useful for sizing players before the video loads. |
| `resolution_tier` | The highest resolution stored: `audio-only`, `720p`, `1080p`, `1440p`, or `2160p`. |
| `video_quality` | Encoding quality tier: `basic`, `plus`, or `premium`. |
| `upload_id` | Reference to the direct upload that created this asset, if applicable. |
| `live_stream_id` | Reference to the live stream that created this asset, if applicable. |
| `passthrough` | A string you set when creating the asset. Useful for storing your own external ID. |
| `tracks` | Array of video, audio, and text track metadata. |

### Uploads

The `mux_uploads` table tracks [direct uploads](/docs/guides/upload-files-directly) from your users' browsers. When an upload completes, Mux creates an asset and sets the `mux_asset_id`.

```postgresql
CREATE TABLE mux_uploads (
mux_upload_id TEXT PRIMARY KEY,
status TEXT NOT NULL DEFAULT 'waiting',
mux_asset_id TEXT REFERENCES mux_assets(mux_asset_id),
timeout INTEGER DEFAULT 3600,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX idx_mux_uploads_status ON mux_uploads (status);
CREATE INDEX idx_mux_uploads_asset_id ON mux_uploads (mux_asset_id);
```

```mysql
CREATE TABLE mux_uploads (
mux_upload_id VARCHAR(255) PRIMARY KEY,
status VARCHAR(50) NOT NULL DEFAULT 'waiting',
mux_asset_id VARCHAR(255),
timeout INTEGER DEFAULT 3600,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (mux_asset_id) REFERENCES mux_assets(mux_asset_id)
);

CREATE INDEX idx_mux_uploads_status ON mux_uploads (status);
CREATE INDEX idx_mux_uploads_asset_id ON mux_uploads (mux_asset_id);
```

```mongodb
db.createCollection("mux_uploads");

db.mux_uploads.createIndex({ mux_upload_id: 1 }, { unique: true });
db.mux_uploads.createIndex({ status: 1 });
db.mux_uploads.createIndex({ mux_asset_id: 1 });

// Example document structure:
// {
//   mux_upload_id: "upload123",
//   status: "asset_created",
//   mux_asset_id: "abc123",
//   timeout: 3600,
//   created_at: ISODate("2026-01-15T10:30:00Z"),
//   updated_at: ISODate("2026-01-15T10:30:00Z")
// }
```



#### Key fields

| Field | Description |
|---|---|
| `mux_upload_id` | The unique identifier for the direct upload. |
| `status` | Upload state: `waiting`, `asset_created`, `errored`, `cancelled`, or `timed_out`. |
| `mux_asset_id` | The asset created from this upload. Only set after status becomes `asset_created`. |
| `timeout` | How long the upload URL remains valid, in seconds (default: 3600). |

### Webhook events

The `mux_webhook_events` table logs every [webhook](/docs/core/listen-for-webhooks) received from Mux. This is essential for debugging sync issues and ensuring you don't process the same event twice.

```postgresql
CREATE TABLE mux_webhook_events (
id SERIAL PRIMARY KEY,
mux_event_id TEXT UNIQUE,
type TEXT NOT NULL,
object_type TEXT,
object_id TEXT,
payload JSONB NOT NULL,
received_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX idx_mux_events_type ON mux_webhook_events (type);
CREATE INDEX idx_mux_events_object ON mux_webhook_events (object_type, object_id);
```

```mysql
CREATE TABLE mux_webhook_events (
id INT AUTO_INCREMENT PRIMARY KEY,
mux_event_id VARCHAR(255) UNIQUE,
type VARCHAR(255) NOT NULL,
object_type VARCHAR(50),
object_id VARCHAR(255),
payload JSON NOT NULL,
received_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_mux_events_type ON mux_webhook_events (type);
CREATE INDEX idx_mux_events_object ON mux_webhook_events (object_type, object_id);
```

```mongodb
db.createCollection("mux_webhook_events");

db.mux_webhook_events.createIndex({ mux_event_id: 1 }, { unique: true, sparse: true });
db.mux_webhook_events.createIndex({ type: 1 });
db.mux_webhook_events.createIndex({ object_type: 1, object_id: 1 });
db.mux_webhook_events.createIndex({ received_at: -1 });

// Example document structure:
// {
//   mux_event_id: "evt_abc123",
//   type: "video.asset.ready",
//   object_type: "asset",
//   object_id: "abc123",
//   payload: { /* full webhook body */ },
//   received_at: ISODate("2026-01-15T10:35:00Z")
// }
```



#### Key fields

| Field | Description |
|---|---|
| `mux_event_id` | Unique event ID from Mux. Use this to deduplicate by checking if the event already exists before processing. |
| `type` | Event type string, like `video.asset.ready` or `video.upload.asset_ready`. |
| `object_type` | The resource type: `asset`, `upload`, or `live_stream`. |
| `object_id` | The ID of the related resource. |
| `payload` | The full webhook body. Store this as JSON so you can re-process events if your handler logic changes. |

### Videos (application metadata)

The `videos` table is optional but recommended. It stores your application-specific metadata, linking Mux assets to your users and adding information that Mux doesn't manage.

```postgresql
CREATE TABLE videos (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
mux_asset_id TEXT NOT NULL REFERENCES mux_assets(mux_asset_id),
user_id TEXT NOT NULL,
title TEXT,
description TEXT,
visibility TEXT NOT NULL DEFAULT 'private',
tags TEXT[] DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX idx_videos_mux_asset_id ON videos (mux_asset_id);
CREATE INDEX idx_videos_user_id ON videos (user_id);
CREATE INDEX idx_videos_visibility ON videos (visibility);
```

```mysql
CREATE TABLE videos (
id CHAR(36) PRIMARY KEY DEFAULT (UUID()),
mux_asset_id VARCHAR(255) NOT NULL,
user_id VARCHAR(255) NOT NULL,
title TEXT,
description TEXT,
visibility VARCHAR(20) NOT NULL DEFAULT 'private',
tags JSON DEFAULT ('[]'),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (mux_asset_id) REFERENCES mux_assets(mux_asset_id)
);

CREATE INDEX idx_videos_mux_asset_id ON videos (mux_asset_id);
CREATE INDEX idx_videos_user_id ON videos (user_id);
CREATE INDEX idx_videos_visibility ON videos (visibility);
```

```mongodb
db.createCollection("videos");

db.videos.createIndex({ mux_asset_id: 1 });
db.videos.createIndex({ user_id: 1 });
db.videos.createIndex({ visibility: 1 });
db.videos.createIndex({ mux_asset_id: 1, user_id: 1 }, { unique: true });

// Example document structure:
// {
//   mux_asset_id: "abc123",
//   user_id: "user_456",
//   title: "My First Video",
//   description: "A short description",
//   visibility: "public",
//   tags: ["tutorial", "getting-started"],
//   created_at: ISODate("2026-01-15T10:30:00Z"),
//   updated_at: ISODate("2026-01-15T10:30:00Z")
// }
```



#### Key fields

| Field | Description |
|---|---|
| `mux_asset_id` | Foreign key to the `mux_assets` table. Links your metadata to the Mux video. |
| `user_id` | Reference to the user in your application who owns this video. |
| `title` / `description` | User-facing metadata for display in your UI. |
| `visibility` | Access control: `public`, `unlisted`, or `private`. Enforce this in your application logic. |
| `tags` | Categorization for filtering and search. |

## Keeping data in sync

Use [Mux webhooks](/docs/core/listen-for-webhooks) to keep your database in sync with Mux. When Mux finishes processing a video, delivers an upload, or encounters an error, it sends a webhook to your server.

The key events to listen for:

| Event | Action |
|---|---|
| `video.asset.created` | Insert a new row in `mux_assets` with status `preparing` |
| `video.asset.ready` | Update the asset's status to `ready` and populate `playback_ids`, `duration`, `tracks` |
| `video.asset.errored` | Update the asset's status to `errored` |
| `video.asset.deleted` | Delete or mark the asset as deleted |
| `video.upload.asset_ready` | Update the upload's `mux_asset_id` and status |
| `video.upload.errored` | Update the upload's status to `errored` |

Here's a simplified example of a webhook handler that syncs asset data:

```javascript
// Example: Webhook handler for syncing Mux assets
app.post('/webhooks/mux', async (req, res) => {
  const event = req.body;

  // 1. Log the event for debugging
  await db.muxWebhookEvents.create({
    mux_event_id: event.id,
    type: event.type,
    object_type: event.object?.type,
    object_id: event.object?.id,
    payload: event,
  });

  // 2. Sync the resource
  if (event.type.startsWith('video.asset.')) {
    const asset = event.data;
    await db.muxAssets.upsert({
      mux_asset_id: asset.id,
      status: asset.status,
      playback_ids: asset.playback_ids,
      duration: asset.duration,
      aspect_ratio: asset.aspect_ratio,
      resolution_tier: asset.resolution_tier,
      tracks: asset.tracks,
      passthrough: asset.passthrough,
    });
  }

  res.status(200).send('ok');
});
```

<Callout type="info">
  Always verify webhook signatures in production to ensure events are genuinely from Mux. See the [webhooks guide](/docs/core/listen-for-webhooks) for details on signature verification.
</Callout>

## Extending for live streaming

If your application supports live streaming, add a `mux_live_streams` table to track stream configurations:

```postgresql
CREATE TABLE mux_live_streams (
mux_live_stream_id TEXT PRIMARY KEY,
status TEXT NOT NULL DEFAULT 'idle',
stream_key TEXT,
playback_ids JSONB DEFAULT '[]',
reconnect_window_seconds INTEGER DEFAULT 60,
active_asset_id TEXT,
recent_asset_ids JSONB DEFAULT '[]',
latency_mode TEXT DEFAULT 'standard',
passthrough TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX idx_mux_live_streams_status ON mux_live_streams (status);
```

```mysql
CREATE TABLE mux_live_streams (
mux_live_stream_id VARCHAR(255) PRIMARY KEY,
status VARCHAR(50) NOT NULL DEFAULT 'idle',
stream_key VARCHAR(255),
playback_ids JSON DEFAULT ('[]'),
reconnect_window_seconds INTEGER DEFAULT 60,
active_asset_id VARCHAR(255),
recent_asset_ids JSON DEFAULT ('[]'),
latency_mode VARCHAR(20) DEFAULT 'standard',
passthrough VARCHAR(255),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

CREATE INDEX idx_mux_live_streams_status ON mux_live_streams (status);
```

```mongodb
db.createCollection("mux_live_streams");

db.mux_live_streams.createIndex({ mux_live_stream_id: 1 }, { unique: true });
db.mux_live_streams.createIndex({ status: 1 });

// Example document structure:
// {
//   mux_live_stream_id: "stream123",
//   status: "idle",
//   stream_key: "sk_us-east-1_abc...",
//   playback_ids: [{ id: "playback123", policy: "public" }],
//   reconnect_window_seconds: 60,
//   active_asset_id: null,
//   recent_asset_ids: ["asset1", "asset2"],
//   latency_mode: "standard",
//   passthrough: null,
//   created_at: ISODate("2026-01-15T10:30:00Z"),
//   updated_at: ISODate("2026-01-15T10:30:00Z")
// }
```



<Callout type="warning">
  The `stream_key` is sensitive. Treat it like a password. Anyone with the stream key can broadcast to your live stream. Store it encrypted or restrict access in your application.
</Callout>

## Next steps

* [Integrate with Supabase](/docs/integrations/supabase): managed Postgres with automatic Mux sync
* [Integrate with Convex](/docs/integrations/convex): real-time database with built-in Mux component
* [Listen for webhooks](/docs/core/listen-for-webhooks): set up webhook handling for real-time sync
* [Secure video playback](/docs/guides/secure-video-playback): protect your content with signed playback IDs
* [API reference](/docs/api-reference): full details on all Mux resource fields


# Integrate with Supabase
The Mux Supabase integration syncs your Mux assets, live streams, and uploads to your Supabase database using webhooks and edge functions.
The `@mux/supabase` package provides a CLI that integrates your Mux account with Supabase. It creates a `mux` schema in your database with tables for assets, live streams, uploads, and webhook events, keeping everything in sync automatically.

## What you'll get

After setup, your Supabase database will have a `mux` schema containing:

* **assets** - Mux video assets and their metadata
* **live\_streams** - Live stream configurations and status
* **uploads** - Direct upload records
* **webhook\_events** - Webhook event history for debugging and auditing

## Prerequisites

Before you begin, make sure you have:

* A [Mux account](https://dashboard.mux.com/signup) with API credentials
* A [Supabase project](https://supabase.com/dashboard)
* The [Supabase CLI](https://supabase.com/docs/guides/cli) installed

## 1. Initialize the integration

First, make sure Supabase is initialized in your project, then run the Mux initialization:

```bash
npx supabase init
npx @mux/supabase init
```

The initialization process will:

1. Create a `mux` schema with tables for assets, live streams, uploads, and webhook events
2. Generate an edge function at `supabase/functions/mux-webhook`
3. Create a `.env` file at `supabase/functions/.env` with placeholder values for your Mux credentials
4. Update `config.toml` to expose the `mux` schema and disable JWT verification for the webhook function

## 2. Configure environment variables

After initialization, update the `.env` file in the `supabase/functions` directory with your actual Mux API credentials:

```bash
MUX_TOKEN_ID=your-mux-token-id
MUX_TOKEN_SECRET=your-mux-token-secret
MUX_WEBHOOK_SECRET=your-webhook-secret
```

You can find your API credentials in the [Mux Dashboard](https://dashboard.mux.com/settings/access-tokens). The webhook secret will be generated when you configure the webhook in step 4.

## 3. Local development

Start your local Supabase instance:

```bash
npx supabase start
```

To test the webhook locally, serve the edge function:

```bash
npx supabase functions serve mux-webhook
```

<Callout type="info">
  To receive webhooks locally, you'll need to expose your local server using a tool like [ngrok](https://ngrok.com/). Configure the ngrok URL as your webhook endpoint in the Mux dashboard.
</Callout>

## 4. Configure the Mux webhook

In the [Mux Dashboard](https://dashboard.mux.com/settings/webhooks), create a new webhook with the following settings:

* **URL**: Your Supabase edge function URL (e.g., `https://your-project.supabase.co/functions/v1/mux-webhook`)
* **Events**: Select the events you want to sync (recommended: all video asset and live stream events)

After creating the webhook, copy the signing secret and add it to your environment variables as `MUX_WEBHOOK_SECRET`.

## 5. Backfill existing data

If you have existing assets in Mux that you want to sync to Supabase, run the backfill command:

```bash
npx @mux/supabase backfill
```

This will import all your existing Mux assets and live streams into your Supabase database.

### Programmatic backfill

You can also run the backfill programmatically using the sync engine:

```typescript
import { MuxSync } from '@mux/sync-engine';

const muxSync = new MuxSync({
  databaseUrl: 'your-supabase-database-url',
  muxTokenId: 'your-mux-token-id',
  muxTokenSecret: 'your-mux-token-secret',
  muxWebhookSecret: 'your-mux-webhook-secret',
});

const result = await muxSync.syncBackfill({ object: 'all' });
```

## 6. Deploy to production

When you're ready to deploy:

1. **Push database migrations**:
   ```bash
   npx supabase db push
   ```

2. **Set production secrets**:
   ```bash
   npx supabase secrets set MUX_TOKEN_ID=your-token-id
   npx supabase secrets set MUX_TOKEN_SECRET=your-token-secret
   npx supabase secrets set MUX_WEBHOOK_SECRET=your-webhook-secret
   ```

3. **Deploy the edge function**:
   ```bash
   npx supabase functions deploy mux-webhook
   ```

4. **Update your Mux webhook URL** in the dashboard to point to your production edge function URL.

## Querying your data

Once synced, you can query your Mux data directly from Supabase. The `mux` schema uses Row Level Security (RLS) that blocks access from `anon` and `authenticated` roles by default, so you'll need to use the service role key for server-side queries:

```typescript
import { createClient } from '@supabase/supabase-js';

const supabase = createClient(
  'your-supabase-url',
  'your-supabase-service-role-key',
  { db: { schema: 'mux' } }
);

// Get all ready assets
const { data: assets } = await supabase
  .from('assets')
  .select('*')
  .eq('status', 'ready');

// Get a specific asset by ID
const { data: asset } = await supabase
  .from('assets')
  .select('*')
  .eq('id', 'asset-id')
  .single();
```

<Callout type="warning">
  The service role key bypasses RLS and should only be used in server-side code. Never expose it in client-side applications. If you need client-side access to Mux data, add your own RLS policies to the tables in the `mux` schema.
</Callout>

## Using with PostgreSQL

If you're using PostgreSQL without Supabase, you can use the `@mux/sync-engine` package directly:

```bash
npm install @mux/sync-engine
```

The sync engine works with any PostgreSQL database and provides the same synchronization capabilities.

## Resources

* [GitHub repository](https://github.com/muxinc/supabase)
* [Supabase documentation](https://supabase.com/docs)
* [Mux API reference](/docs/api-reference)


# Integrate with Convex
The Mux Convex component syncs your Mux assets, live streams, and uploads to your Convex database with real-time updates via webhooks.
The `@mux/convex` package is a reusable Convex component that bridges Mux video services with the Convex backend. It provides database tables for Mux resources, webhook handling, and query helpers for building video applications.

## What you'll get

After setup, your Convex database will have tables for:

* **assets** - Mux video assets and their metadata
* **uploads** - Direct upload records
* **liveStreams** - Live stream configurations and status
* **events** - Webhook event history
* **videoMetadata** - App-level metadata (user ID, title, description, visibility, tags, custom fields)

## Prerequisites

Before you begin, make sure you have:

* A [Mux account](https://dashboard.mux.com/signup) with API credentials
* A [Convex project](https://dashboard.convex.dev) with a `convex/` directory already initialized
* The Convex CLI installed (`npm install -g convex`)
* A TypeScript web application (e.g., [Next.js](https://docs.convex.dev/quickstart/nextjs), [React](https://docs.convex.dev/quickstart/react), or any [Convex-supported framework](https://docs.convex.dev/quickstarts))

<Callout type="info">
  New to Convex? Follow the [Convex quickstart](https://docs.convex.dev/quickstarts) for your framework first, then come back here to add Mux integration.
</Callout>

## 1. Install packages

Install the required packages in your project:

```bash
npm install convex @mux/convex @mux/mux-node
```

## 2. Generate project files

Run the initialization script to generate the required Convex files:

```bash
npx @mux/convex init --component-name mux
```

This creates four files in your `convex` directory:

* `convex.config.ts` - Mounts the Mux component into your Convex app
* `migrations.ts` - Backfill function for syncing existing Mux assets
* `muxWebhook.ts` - Webhook handler for receiving Mux events
* `http.ts` - HTTP route that exposes the webhook endpoint

If any of these files already exist, the CLI will skip them. Use `--force` to overwrite existing files.

<Callout type="info">
  If you already have a `convex/convex.config.ts` or `convex/http.ts`, use `--skip-config` or `--skip-http` to skip those files and manually integrate the Mux component into your existing configuration.
</Callout>

## 3. Set environment variables

Configure your Mux API credentials in Convex:

```bash
npx convex env set MUX_TOKEN_ID your-mux-token-id
npx convex env set MUX_TOKEN_SECRET your-mux-token-secret
```

You can find your API credentials in the [Mux Dashboard](https://dashboard.mux.com/settings/access-tokens).

## 4. Start development and backfill

Start the Convex development server:

```bash
npx convex dev
```

In another terminal, run the backfill migration to sync your existing Mux assets:

```bash
npx convex run migrations:backfillMux '{}'
```

This will import your existing Mux assets into the Convex `assets` and `videoMetadata` tables. You can customize the backfill with options:

```bash
npx convex run migrations:backfillMux '{"maxAssets": 500, "defaultUserId": "my-user-id", "includeVideoMetadata": true}'
```

<Callout type="info">
  The backfill currently syncs Mux assets only. Live streams and uploads will be synced in real time once you configure the webhook in the next step.
</Callout>

## 5. Configure the Mux webhook

In the [Mux Dashboard](https://dashboard.mux.com/settings/webhooks), create a new webhook with your Convex HTTP endpoint as the URL:

```
https://your-deployment.convex.site/mux/webhook
```

Mux will send all events to this endpoint. The component automatically handles routing asset, live stream, and upload events to the appropriate tables and ignores unsupported event types.

After creating the webhook, copy the signing secret and add it to your Convex environment:

```bash
npx convex env set MUX_WEBHOOK_SECRET your-webhook-signing-secret
```

## Verify the setup

Check your [Convex dashboard](https://dashboard.convex.dev) to verify the tables are populated:

1. Navigate to your project's Data tab
2. You should see the Mux tables: `assets`, `uploads`, `liveStreams`, `events`, and `videoMetadata`
3. Upload a test video in Mux and verify it appears in your Convex database

## Querying your data

The component syncs Mux data into your Convex database via webhooks. To access that data, you wrap the component's built-in queries with your own Convex functions, then call those functions from your frontend.

### Define your queries

Create a new file in your `convex` directory (e.g., `convex/videoQueries.ts`) to wrap the component queries:

```typescript
import { query } from './_generated/server';
import { components } from './_generated/api';
import { v } from 'convex/values';

// Returns an array of asset objects, each containing:
// muxAssetId, status, playbackIds, durationSeconds, aspectRatio, tracks, etc.
export const listAssets = query({
  handler: async (ctx) => {
    return await ctx.runQuery(components.mux.catalog.listAssets, {});
  },
});

// Returns a single asset object or null
export const getAsset = query({
  args: { muxAssetId: v.string() },
  handler: async (ctx, args) => {
    return await ctx.runQuery(components.mux.catalog.getAssetByMuxId, {
      muxAssetId: args.muxAssetId,
    });
  },
});

// Returns an array of { metadata, asset } pairs for a given user
export const getUserVideos = query({
  args: { userId: v.string() },
  handler: async (ctx, args) => {
    return await ctx.runQuery(components.mux.videos.listVideosForUser, {
      userId: args.userId,
    });
  },
});
```

### Use queries in your frontend

Call these queries from your React components using Convex's `useQuery` hook:

```typescript
import { useQuery } from 'convex/react';
import { api } from '../convex/_generated/api';

function VideoList() {
  const assets = useQuery(api.videoQueries.listAssets);

  if (!assets) return <div>Loading...</div>;

  return (
    <ul>
      {assets.map((asset) => (
        <li key={asset.muxAssetId}>
          {asset.muxAssetId} — {asset.status}
        </li>
      ))}
    </ul>
  );
}
```

These queries are reactive — your UI will update automatically when the underlying data changes in Convex, such as when a webhook updates an asset's status.

### Available queries

The component exposes the following queries through `components.mux`:

| Query | Description |
|---|---|
| `catalog.listAssets` | List all synced assets, sorted by most recently updated |
| `catalog.getAssetByMuxId` | Get a single asset by its Mux asset ID |
| `catalog.listLiveStreams` | List all synced live streams |
| `catalog.getLiveStreamByMuxId` | Get a single live stream by its Mux live stream ID |
| `catalog.listUploads` | List all synced direct uploads |
| `catalog.getUploadByMuxId` | Get a single upload by its Mux upload ID |
| `catalog.listRecentEvents` | List recent webhook events |
| `videos.listVideosForUser` | List assets with app-level metadata for a specific user |
| `videos.getVideoByMuxAssetId` | Get an asset with its associated metadata |

All list queries accept an optional `limit` parameter (default: 50).

## Adding video metadata

The catalog queries above return Mux-level data synced via webhooks. The video metadata system lets you layer your own app-level data — like titles, descriptions, user ownership, and visibility — on top of those synced assets.

```typescript
import { mutation } from './_generated/server';
import { components } from './_generated/api';
import { v } from 'convex/values';

export const setVideoMetadata = mutation({
  args: {
    muxAssetId: v.string(),
    userId: v.string(),
    title: v.optional(v.string()),
    description: v.optional(v.string()),
    visibility: v.optional(
      v.union(v.literal('public'), v.literal('private'), v.literal('unlisted'))
    ),
    tags: v.optional(v.array(v.string())),
  },
  handler: async (ctx, args) => {
    await ctx.runMutation(components.mux.videos.upsertVideoMetadata, {
      muxAssetId: args.muxAssetId,
      userId: args.userId,
      title: args.title,
      description: args.description,
      visibility: args.visibility,
      tags: args.tags,
    });
  },
});
```

Once metadata is set, queries like `videos.listVideosForUser` and `videos.getVideoByMuxAssetId` will return both the Mux asset data and your app-level metadata together.

## Important notes

<Callout type="info">
  The backfill is a one-time synchronization. After that, webhooks maintain near real-time updates between Mux and Convex.
</Callout>

<Callout type="warning">
  Make sure to use consistent component names across all configuration steps. If you change the component name from `mux`, regenerate the wrapper files with the matching name: `npx @mux/convex init --component-name yourName --force`
</Callout>

## Resources

* [GitHub repository](https://github.com/Joshalphonse/mux-convex-component)
* [Convex documentation](https://docs.convex.dev)
* [Mux API reference](/docs/api-reference)
