<SYSTEM>This is introductory concepts for developers using Mux.</SYSTEM>

# Stream videos in five minutes
Upload and play back your video files in your application using Mux in five minutes or less.
## 1. Get an API Access Token

The Mux Video API uses a token key pair that consists of a **Token ID** and **Token Secret** for authentication. If you haven't already, generate a new Access Token in the [Access Token settings](https://dashboard.mux.com/settings/access-tokens) of your Mux account dashboard.

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

You'll be presented with a form to create your new Access Token.

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

* **Access Tokens** belong to an **Environment** — a container for the various Access Tokens, Signing Keys, and assets that you'll come to add to Mux. For this guide, you can keep the **Production** environment selected.
* **Access Tokens** can have varying permissions to control what kinds of changes they have the ability to make. For this guide, your **Access Token** should have Mux Video **Read** and **Write** permissions.
* You can give your **Access Token** an internal-only name like "Onboarding" so you know where you've used it within your application.

Now, click the **Generate token** button.

You'll be presented with your new **Access Token ID** and **Secret Key**.

<Image src="/docs/images/settings-generated-access-token.png" width={545} height={233} alt="Mux access token environment" />

Once you have your new **Access Token ID** and **Secret Key**, you're ready to upload your first video.

## 2. POST a video

Videos stored in Mux are called <ApiRefLink href="/docs/api-reference/video/assets">assets</ApiRefLink>. To create your first video asset, you need to send a <ApiRefLink href="/docs/api-reference/video/assets/create-asset">POST request to the /assets endpoint</ApiRefLink> and set the `input` value to the URL of a video file that's accessible online.

Here are a few demo videos you can use that are stored on common cloud storage services:

* Amazon S3: https://muxed.s3.amazonaws.com/leds.mp4
* Google Drive: https://drive.google.com/uc?id=13ODlJ-Dxrd7aJ7jy6lsz3bwyVW-ncb3v
* Dropbox: https://www.dropbox.com/scl/fi/l2sm1zyk6pydtosk3ovwo/get-started.mp4?rlkey=qjb34b0b7wgjbs5xj9vn4yevt\&dl=0

To start making API requests to Mux, you might want to install one of our officially supported API SDKs. These are lightweight wrapper libraries that use your API credentials to make authenticated HTTP requests to the Mux API.

```elixir

# mix.exs
def deps do
  [
    {:mux, "~> 1.8.0"}
  ]
end

```

```go

go get github.com/muxinc/mux-go

```

```node

# npm
npm install @mux/mux-node --save

# yarn
yarn add @mux/mux-node

```

```php

# composer.json
{
    "require": {
        "muxinc/mux-php": ">=0.0.1"
    }
}

```

```python

# 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

```

```ruby

gem 'mux_ruby'

```



<Callout type="info">
  For an example of how to make API Requests from your local environment, see the [Make API Requests](/docs/core/make-api-requests) guide.
</Callout>

<CodeExamples product="video" example="createAsset" />

The response will include an **Asset ID** and a **Playback ID**.

* Asset IDs are used to manage assets using `api.mux.com` (e.g. to read or delete an asset).
* <ApiRefLink href="/docs/api-reference/video/playback-id">Playback IDs</ApiRefLink> are used to stream an asset to a video player through `stream.mux.com`. You can add multiple playback IDs to an asset to create playback URLs with different viewing permissions, and you can delete playback IDs to remove access without deleting the asset.

```json
{
  "data": {
    "status": "preparing",
    "playback_ids": [
      {
        "policy": "public",
        "id": "TXjw00EgPBPS6acv7gBUEJ14PEr5XNWOe"
      }
    ],
    "video_quality": "basic",
    "mp4_support": "none",
    "master_access": "none",
    "id": "01itgOBvgjAbES7Inwvu4kEBtsQ44HFL6",
    "created_at": "1607876845"
  }
}
```

<Callout type="info">
  Mux does not store the original file in its exact form, so if your original quality files are important to you, don't delete them after submitting them to Mux.
</Callout>

## 3. Wait for \`ready\`

As soon as you make the `POST` request, Mux begins downloading and processing the video. For shorter files, this often takes just a few seconds. Very large files over poor connections may take a few minutes (or longer).

When the video is ready for playback, the asset `status` changes to `ready`. You should wait until the asset status is `ready` before you attempt to play the video.

The best way to be notified of asset status updates is via **webhooks**. Mux can send a webhook notification as soon as the asset is ready. See the [webhooks guide](/docs/core/listen-for-webhooks) for details.

If you can't use webhooks for some reason, you can manually **poll** the <ApiRefLink href="/docs/api-reference/video/assets/get-asset">asset API</ApiRefLink> to see asset status. Note that this only works at low volume. Try this example:

## Try an example request

<CodeExamples product="video" example="retrieveAsset" />

Please don't poll this API more than once per second.

## 4. Watch your Video

To play back an asset, create a playback URL using the `PLAYBACK_ID` you received when you created the asset.

```curl
https://stream.mux.com/{PLAYBACK_ID}.m3u8
```

## Preview in a player

```android

implementation 'com.google.android.exoplayer:exoplayer-hls:2.X.X'

// Create a player instance.
SimpleExoPlayer player = new SimpleExoPlayer.Builder(context).build();
// Set the media item to be played.
player.setMediaItem(MediaItem.fromUri("https://stream.mux.com/{PLAYBACK_ID}.m3u8"));
// Prepare the player.
player.prepare();

```

```embed

<iframe
  src="https://player.mux.com/{PLAYBACK_ID}?metadata-video-title=Test%20video%20title&metadata-viewer-user-id=user-id-007"
  style="aspect-ratio: 16/9; width: 100%; border: 0;"
  allow="accelerometer; gyroscope; autoplay; encrypted-media; picture-in-picture;"
  allowfullscreen="true"
></iframe>

```

```html

<script src="https://cdn.jsdelivr.net/npm/@mux/mux-player" defer></script>

<mux-player
  playback-id="{PLAYBACK_ID}"
  metadata-video-title="Test video title"
  metadata-viewer-user-id="user-id-007"
></mux-player>

```

```react

import MuxPlayer from '@mux/mux-player-react';

export default function VideoPlayer() {
  return (
    <MuxPlayer
      playbackId="{PLAYBACK_ID}"
      metadata={{
        video_id: "video-id-54321",
        video_title: "Test video title",
        viewer_user_id: "user-id-007",
      }}
    />
  );
}

```

```swift

import SwiftUI
import AVKit

private let playbackURL: URL = {
    guard let url = URL(string: "https://stream.mux.com/{PLAYBACK_ID}.m3u8") else {
        preconditionFailure("Invalid playback URL")
    }
    return url
}()

struct ContentView: View {
    private let player = AVPlayer(url: playbackURL)

    var body: some View {
        // VideoPlayer comes from SwiftUI.
        VideoPlayer(player: player)
            .onAppear {
                player.play()
            }
    }
}

```



See the [playback guide](/docs/guides/play-your-videos) for more information about how to integrate with a video player.

## Preview with `stream.new`

[Stream.new](https://stream.new/) is an open source project by Mux that allows you to add a video and get a shareable link to stream it.

Go to `stream.new/v/{PLAYBACK_ID}` to preview your video streaming. This URL is shareable and automatically generated using the video playback ID. Copy the link below and open it in a browser to view your video.

```
https://stream.new/v/{PLAYBACK_ID}
```

After you have everything working [integrate Mux Data](/docs/guides/track-your-video-performance) with your player for monitoring playback performance.

## 5. Manage your Mux assets

After you have assets created in your Mux environment, you may find some of these other endpoints handy:

* <ApiRefLink href="/docs/api-reference/video/assets/create-asset">Create an asset</ApiRefLink>
* <ApiRefLink href="/docs/api-reference/video/assets/list-assets">List assets</ApiRefLink>
* <ApiRefLink href="/docs/api-reference/video/assets/get-asset">Retrieve an asset</ApiRefLink>
* <ApiRefLink href="/docs/api-reference/video/assets/delete-asset">Delete an asset</ApiRefLink>
* <ApiRefLink href="/docs/api-reference/video/assets/get-asset-input-info">Retrieve asset input info</ApiRefLink>
* <ApiRefLink href="/docs/api-reference/video/assets/create-asset-playback-id">Create asset playback ID</ApiRefLink>
* <ApiRefLink href="/docs/api-reference/video/assets/get-asset-playback-id">Retrieve asset playback ID</ApiRefLink>
* <ApiRefLink href="/docs/api-reference/video/assets/delete-asset-playback-id">Delete asset playback ID</ApiRefLink>
* <ApiRefLink href="/docs/api-reference/video/assets/update-asset-mp4-support">Update MP4 support on asset</ApiRefLink>
* <ApiRefLink href="/docs/api-reference/video/assets/update-asset-master-access">Update master access on asset</ApiRefLink>
* <ApiRefLink href="/docs/api-reference/video/assets/create-asset-track">Update asset track</ApiRefLink>
* <ApiRefLink href="/docs/api-reference/video/assets/delete-asset-track">Delete an asset track</ApiRefLink>

More Video methods and descriptions are available at the <ApiRefLink href="/docs/api-reference/video">API Docs</ApiRefLink>.

# Next Steps

<GuideCard
  title="Play your videos"
  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 previewing your videos with Thumbnails and Storyboards"
  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"},
  ]}
/>


# Mux fundamentals
A reference guide covering the essential concepts, terminology, and components you need to understand when building with Mux.
Whether you're just getting started with Mux or need a quick refresher on how the pieces fit together, this guide covers the fundamental concepts you'll encounter when building video, audio, and live streaming applications.

# Quick reference

| Term | Description |
| :--- | :---------- |
| [**Organization**](#organizations) | The top-level account container. You can belong to multiple organizations, each with its own billing, team members, and environments. |
| [**Environment**](#environments) | A container within an organization for organizing your Mux resources (assets, live streams, API tokens, etc.). Each organization can have multiple environments. |
| [**Access Token**](#access-tokens) | A credential pair (Token ID + Token Secret) used to authenticate API requests. Scoped to a single environment. |
| [**Asset**](#assets) | A video or audio file that has been uploaded to Mux and processed for streaming playback. |
| [**Playback ID**](#playback-ids) | A unique identifier used to stream an asset or live stream to viewers. |
| [**Live Stream**](#live-streams) | A resource representing a live broadcast that can receive RTMP/SRT input and deliver to viewers. |
| [**Stream Key**](#live-streams) | A secret credential that allows a broadcaster to push video to a specific live stream. |
| [**Signing Key**](#signing-keys) | A public/private key pair used to create signed tokens (JWTs) for secure playback. |
| [**Webhook**](#webhooks) | An HTTP callback that Mux sends to your server when events occur (e.g., asset ready, live stream started). |

# Organizations

An **organization** is your top-level Mux account. It's the highest container in the Mux hierarchy and contains everything else: environments, team members, and billing settings.

Key things to know about organizations:

* **You can belong to multiple organizations.** This is useful if you work with different companies or clients, each with their own Mux account.
* **Each organization has its own billing.** Usage charges are tracked and billed per organization.
* **Team members are managed at the organization level.** You can invite collaborators and assign roles (Admin, Member) within each organization.
* **Organizations contain environments.** All your media resources live inside environments, which live inside organizations.

You can switch between organizations and create new ones from the [Mux Dashboard](https://dashboard.mux.com/organizations).

# Environments

An **environment** is a container within an organization for organizing your Mux resources. Each environment has its own isolated set of assets, live streams, access tokens, signing keys, and webhooks.

Common use cases for multiple environments:

* Separate **development** and **production** resources
* Isolate resources for different websites or domains (e.g., `site1.com`, `site2.com`)
* Organize by project or use case (e.g., CMS media, marketing site, customer uploads)
* Keep test data separate from production content

<Callout type="warning" title="Environment isolation">
  Resources are scoped to their environment. An access token created in Development cannot be used to manage assets in Production, and webhooks configured for one environment won't fire for events in another.
</Callout>

You can view and manage environments in the [Mux Dashboard](https://dashboard.mux.com/organizations).

# Access Tokens

**Access tokens** are credentials that authenticate your API requests to Mux. Each token consists of two parts:

| Part | Description |
| :--- | :---------- |
| **Token ID** | The "username" portion of your credential. Safe to log (but not expose publicly). |
| **Token Secret** | The "password" portion. Keep this secure and never expose it in client-side code. |

<Callout type="info" title="Secret recovery">
  Mux only stores a hash of your token secret. If you lose it, you'll need to create a new access token.
</Callout>

<Callout type="warning" title="Server-side only">
  Mux API requests must be made from a server, not from client-side code. The API does not support CORS, and exposing your credentials in a browser or mobile app is a security risk.
</Callout>

## Token permissions

When creating an access token, you configure which permissions it has:

| Permission | Use case |
| :--------- | :------- |
| **Mux Video Read** | Retrieve information about assets and live streams |
| **Mux Video Write** | Create, update, and delete assets and live streams |
| **Mux Data Read** | Access playback performance metrics |
| **Mux Data Write** | Create Data annotations |
| **System Read** | View signing keys and other system resources |
| **System Write** | Create and manage signing keys |

For most use cases when getting started, you'll want **Mux Video Read** and **Write** permissions.

You can create and manage access tokens in the [Mux Dashboard](https://dashboard.mux.com/settings/access-tokens).

**Learn more:** [Make API requests](/docs/core/make-api-requests) | [Use an SDK](/docs/core/sdks)

# Assets

An **asset** is a video or audio file that has been ingested into Mux and processed for adaptive bitrate streaming. When you create an asset, Mux:

1. Downloads the file from your provided URL (or receives it via [direct upload](/docs/guides/upload-files-directly))
2. Transcodes it into multiple quality levels
3. Packages it for HLS streaming
4. Generates a unique **asset ID**

```json
// Example asset response
{
  "data": {
    "id": "01itgOBvgjAbES7Inwvu4kEBtsQ44HFL6",
    "status": "ready",
    "playback_ids": [
      {
        "id": "TXjw00EgPBPS6acv7gBUEJ14PEr5XNWOe",
        "policy": "public"
      }
    ],
    "duration": 120.5,
    "aspect_ratio": "16:9"
  }
}
```

## Asset status lifecycle

Assets progress through several statuses:

| Status | Description |
| :----- | :---------- |
| `preparing` | Mux is downloading and processing the file |
| `ready` | The asset is ready for playback |
| `errored` | Something went wrong during processing |

Rather than polling the API to check status, use [webhooks](/docs/core/listen-for-webhooks) to be notified when an asset is ready.

**Learn more:** [Stream videos in five minutes](/docs/core/stream-video-files) | <ApiRefLink href="/docs/api-reference/video/assets">Assets API</ApiRefLink>

# Playback IDs

A **playback ID** is what you use to actually stream content to viewers. While asset IDs are used to *manage* your content (via `api.mux.com`), playback IDs are used to *stream* your content (via `stream.mux.com`).

```
https://stream.mux.com/{PLAYBACK_ID}.m3u8
```

## Playback policies

Each playback ID has a policy that controls how it can be accessed:

| Policy | Description |
| :----- | :---------- |
| `public` | Anyone with the URL can access the content |
| `signed` | Viewers need a valid JWT token to watch |

An asset can have multiple playback IDs with different policies. This lets you, for example, have a public playback ID for trailers and a signed playback ID for the full content.

<Callout type="info" title="Multiple playback IDs">
  You can add and remove playback IDs without affecting the underlying asset. This is useful for revoking access without re-encoding your content.
</Callout>

**Learn more:** [Play your videos](/docs/guides/play-your-videos) | [Secure video playback](/docs/guides/secure-video-playback)

# Live streams

A **live stream** represents a live broadcast channel. Unlike assets (which are created from existing files), live streams receive real-time input and deliver it to viewers with low latency.

## Key live stream components

| Component | Description |
| :-------- | :---------- |
| **Stream Key** | A secret credential broadcasters use to connect their encoder to Mux |
| **RTMP URL** | The ingest endpoint (`rtmp://global-live.mux.com:5222/app`) |
| **SRT URL** | Alternative ingest endpoint for SRT protocol |
| **Playback ID** | Used to stream to viewers (same concept as asset playback IDs) |

<Callout type="warning" title="Keep stream keys secret">
  Anyone with your stream key can broadcast to your live stream. Treat it like a password.
</Callout>

## Live stream lifecycle

| Status | Description |
| :----- | :---------- |
| `idle` | No one is broadcasting; waiting for input |
| `active` | A broadcaster is connected and viewers can watch |
| `disabled` | The live stream has been disabled and won't accept connections |

When a live stream ends, Mux automatically creates a new asset from the recording (if recording is enabled).

**Learn more:** [Configure broadcast software](/docs/guides/configure-broadcast-software) | [Handle disconnections](/docs/guides/handle-live-stream-disconnects) | <ApiRefLink href="/docs/api-reference/video/live-streams">Live Streams API</ApiRefLink>

# Signing keys

**Signing keys** are cryptographic key pairs used to generate JWTs (JSON Web Tokens) for [secure video playback](/docs/guides/secure-video-playback). When you have assets or live streams with `signed` playback policies, you need signing keys to create valid playback tokens.

| Component | Description |
| :-------- | :---------- |
| **Key ID** | A unique identifier for the signing key |
| **Private Key** | Used by your server to sign JWTs. Keep this secret. |

Your server uses the private key to create short-lived tokens that grant access to specific content. The token can include claims for:

* **Expiration time** - When the token becomes invalid
* **Playback restrictions** - Additional rules like allowed domains

<Callout type="info" title="Not the same as access tokens">
  Signing keys and access tokens serve different purposes:

  * **Access tokens** authenticate your server-to-Mux API requests
  * **Signing keys** create tokens that authenticate viewer playback requests
</Callout>

You can create and manage signing keys in the [Mux Dashboard](https://dashboard.mux.com/settings/signing-keys).

**Learn more:** [Secure video playback](/docs/guides/secure-video-playback) | <ApiRefLink href="/docs/api-reference/system/signing-keys">Signing Keys API</ApiRefLink>

# Webhooks

**Webhooks** are HTTP callbacks that Mux sends to your application when events occur. Instead of repeatedly polling the API to check if an asset is ready, you configure a webhook URL and Mux notifies you automatically.

Common webhook events:

| Event | Description |
| :---- | :---------- |
| `video.asset.ready` | An asset has finished processing and is ready for playback |
| `video.asset.errored` | An asset failed to process |
| `video.live_stream.active` | A live stream has started broadcasting |
| `video.live_stream.idle` | A live stream has stopped broadcasting |
| `video.upload.asset_created` | A direct upload has completed and created an asset |

<Callout type="warning" title="Environment-scoped">
  Webhooks are configured per environment. Make sure your webhook is set up in the same environment where your resources are created.
</Callout>

**Learn more:** [Listen for webhooks](/docs/core/listen-for-webhooks) | [Verify webhook signatures](/docs/core/verify-webhook-signatures)

# IDs at a glance

Mux uses several different types of identifiers. Here's a quick reference:

| ID Type | Format Example | Purpose |
| :------ | :------------- | :------ |
| **Organization ID** | `abc123` | Identify your organization |
| **Environment ID** | `j0863n` | Identify specific environments within an organization |
| **Asset ID** | `01itgOBvgj...` | Identify and manage assets via the API |
| **Playback ID** | `TXjw00EgPB...` | Stream content to viewers |
| **Live Stream ID** | `aA02skpHX...` | Identify and manage live streams via the API |
| **Upload ID** | `OA02dANZ...` | Track direct upload status |
| **Token ID** | `44c819de-4add-...` | Identify access tokens (part of API auth) |
| **Signing Key ID** | `JjPXgkqO...` | Identify signing keys for JWT creation |

# SDKs

Mux provides official SDKs for several languages that handle authentication and make it easier to work with the API:

* [Node.js](/docs/integrations/mux-node-sdk)
* [Python](/docs/integrations/mux-python-sdk)
* [Ruby](/docs/integrations/mux-ruby-sdk)
* [PHP](/docs/integrations/mux-php-sdk)
* [Java](/docs/integrations/mux-java-sdk)
* [C# / .NET](/docs/integrations/mux-csharp-sdk)
* [Elixir](/docs/integrations/mux-elixir-sdk)

For client-side playback, see [Mux Player](/docs/guides/mux-player-web) and the various player SDK guides.

**Learn more:** [Use an SDK](/docs/core/sdks)

# API and webhook specifications

Mux publishes machine-readable specifications for both the API and webhook events:

| Specification | URL | Description |
| :------------ | :-- | :---------- |
| **Combined spec** | [`mux.com/full-combined-spec.json`](https://www.mux.com/full-combined-spec.json) | All API endpoints and webhook events in one spec |
| **API spec** | [`mux.com/api-spec.json`](https://www.mux.com/api-spec.json) | Core API endpoints only |
| **Webhook spec** | [`mux.com/webhook-spec.json`](https://www.mux.com/webhook-spec.json) | Webhook event schemas only |
| **Image API spec** | [`mux.com/image-spec.json`](https://www.mux.com/image-spec.json) | Thumbnail, animated GIF, and storyboard endpoints |
| **Streaming API spec** | [`mux.com/stream-spec.json`](https://www.mux.com/stream-spec.json) | HLS and MP4 streaming playback endpoints |
| **Engagement Counts spec** | [`mux.com/stats-spec.json`](https://www.mux.com/stats-spec.json) | Real-time view and viewer count endpoints |

These are useful for generating API clients, importing into tools like [Postman](/docs/core/postman), validating webhook payloads, or integrating with any tooling that supports OpenAPI. Use the combined spec if you want everything in one file.

# What's next?

Now that you understand the fundamentals, here are some recommended next steps:

<GuideCard
  title="Stream videos in five minutes"
  description="Upload your first video to Mux and play it back in your application."
  links={[
    {title: "Read the guide", href: "/docs/core/stream-video-files"},
  ]}
/>

<GuideCard
  title="Listen for webhooks"
  description="Set up webhooks to receive real-time notifications when events occur."
  links={[
    {title: "Read the guide", href: "/docs/core/listen-for-webhooks"},
  ]}
/>

<GuideCard
  title="Secure video playback"
  description="Learn how to protect your content with signed URLs and playback restrictions."
  links={[
    {title: "Read the guide", href: "/docs/guides/secure-video-playback"},
  ]}
/>


# Getting started for AI agents
A reference for LLMs and AI agents writing code against the Mux API.
# Getting started for AI agents

This guide is written for LLMs and AI coding agents. It contains everything you need to write working code against the Mux API on the first try.

## CLI

The [Mux CLI](/docs/integrations/mux-cli) (`@mux/cli`) lets you manage Mux resources directly from the terminal. It is useful for quick operations, scripting, automation, and CI/CD pipelines.

Install:

```bash
npm install -g @mux/cli    # global install
# or run directly without installing:
npx @mux/cli
# or install via Homebrew:
brew install muxinc/tap/mux
```

After installing, authenticate with `mux login` or set `MUX_TOKEN_ID` and `MUX_TOKEN_SECRET` environment variables. Always pass `--agent` to optimize output for AI agents (includes JSON output).

Common commands:

| Command | What it does |
| :--- | :--- |
| `mux assets create --input-url URL` | Create an asset from a URL |
| `mux assets create --file video.mp4` | Upload a local file |
| `mux assets list` | List assets |
| `mux assets get ${ASSET_ID}` | Get asset details |
| `mux assets delete ${ASSET_ID}` | Delete an asset |
| `mux live-streams create` | Create a live stream |
| `mux uploads create` | Create a direct upload URL |
| `mux sign ${PLAYBACK_ID}` | Generate a signed playback URL |

Use `--wait` to block until an asset is ready:

```bash
mux assets create --input-url "https://example.com/video.mp4" --wait --agent
```

For the full list of commands, see the [CLI documentation](/docs/integrations/mux-cli).

## Common resources

* [Authentication and making API requests](/docs/core/make-api-requests) — how to authenticate with the Mux API
* [Rate limits](/docs/core/make-api-requests#api-rate-limits) — API rate limit details
* [Full API reference](/docs/api-reference/video) — complete endpoint documentation
* [Webhooks guide](/docs/core/listen-for-webhooks) — listen for events like `video.asset.ready`
* [Mux Player guide](/docs/guides/play-your-videos) — the easiest way to play Mux videos
* [Direct uploads guide](/docs/guides/upload-files-directly) — let users upload video files from the browser
* [Stream video files guide](/docs/core/stream-video-files) — ingest a video, wait for it to be ready, get a playback URL
* [Live streaming guide](/docs/guides/start-live-streaming) — set up live streams
* [Secure video playback](/docs/guides/secure-video-playback) — signed playback for private content
* [Static MP4 renditions guide](/docs/guides/enable-static-mp4-renditions) — downloadable MP4/M4A files
* [MCP Server](/docs/integrations/mcp-server) — use Mux from AI tools that support MCP
* [Docs for LLMs](/docs/core/llms-txt) — machine-readable documentation bundles
* [OpenAPI spec](https://www.mux.com/api-spec.json) — full API specification

## Instructions for your human

Before you can make API calls, your user needs to set up a Mux account and provide you with API credentials.

1. **Sign up for Mux** at the [Mux dashboard](https://dashboard.mux.com/signup). No credit card is required. The free tier includes enough usage to develop and test.
2. **Go to the [Mux dashboard](https://dashboard.mux.com)**, then navigate to **Settings → Access Tokens**.
3. **Create an API access token** with all permissions — **Mux Video**, **Mux Data**, and **System** — with read and write access.
4. **Save the Access Token ID and Access Token Secret.** The secret is only shown once.

Store the credentials as environment variables:

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

For details on how authentication works, see the [make API requests guide](/docs/core/make-api-requests).

<Callout type="warning">
  Never expose API credentials in client-side code. All Mux API calls must be made from a server.
</Callout>

## Two main ways to use Mux

### Website embed

This is for when you have a small, constrained number of videos — a hero video or background video on your homepage, a demo reel, or a few dozen videos across your site. The key characteristic is that the set of videos doesn't change often and is manageable enough to hardcode.

In this case, you can hardcode playback IDs directly in your code or extract them into a JSON config file with metadata:

```json
{
  "videos": [
    { "title": "Hero Video", "playbackId": "TXjw00EgPBPS6acv7gBUEJ14PEr5XNWOe" },
    { "title": "Product Demo", "playbackId": "a4nOgR00sKz6cMWLeM5skT8ePBn7U6gC5" }
  ]
}
```

Then use [Mux Player](/docs/guides/play-your-videos) to embed each video:

```jsx
import MuxPlayer from '@mux/mux-player-react';

<MuxPlayer playbackId="TXjw00EgPBPS6acv7gBUEJ14PEr5XNWOe" />
```

To create your assets and get playback IDs, use the CLI (`mux assets create --input-url URL --wait --json`) or the [stream video files guide](/docs/core/stream-video-files).

### User uploaded

This is for when videos are uploaded dynamically as part of your application. Common scenarios include:

* **Admin-managed content** — an admin area where authorized users upload and manage videos (e.g., a course platform, media library, or CMS)
* **User-generated content (UGC)** — end users upload their own videos (e.g., a social platform, portfolio site, or community forum)
* **Programmatic ingestion** — videos are created automatically from external sources or pipelines

For these use cases, you will need to:

1. **Accept uploads** — use [direct uploads](/docs/guides/upload-files-directly) to let users upload video files from the browser, or create assets server-side from URLs using the [stream video files guide](/docs/core/stream-video-files)
2. **Listen for events** — use [webhooks](/docs/core/listen-for-webhooks) to know when a video is ready for playback (`video.asset.ready`), when it errors, or when it's deleted
3. **Persist video data** — store asset IDs, playback IDs, status, and metadata in your database (see [Persisting Mux data](#persisting-mux-data) below)
4. **Play videos** — use [Mux Player](/docs/guides/play-your-videos) with the stored playback ID

## SDKs

Official server-side SDKs:

| Language | Package | Docs |
| :--- | :--- | :--- |
| Node.js | `@mux/mux-node` | [Guide](/docs/integrations/mux-node-sdk) |
| Python | `mux_python` | [Guide](/docs/integrations/mux-python-sdk) |
| Ruby | `mux_ruby` | [Guide](/docs/integrations/mux-ruby-sdk) |
| PHP | `mux-php` | [Guide](/docs/integrations/mux-php-sdk) |
| Go | `mux-go` | [GitHub](https://github.com/muxinc/mux-go) |
| Java | `com.mux:mux-sdk-java` | [Guide](/docs/integrations/mux-java-sdk) |
| C# | `Mux.Csharp.Sdk` | [Guide](/docs/integrations/mux-csharp-sdk) |
| Elixir | `mux` | [Guide](/docs/integrations/mux-elixir-sdk) |

## Sensible defaults

Unless the user specifies otherwise, use these values:

| Parameter | Default to use | Notes |
| :--- | :--- | :--- |
| `playback_policy` | `["public"]` | Use `"signed"` only if the user needs secure/private playback |
| `video_quality` | `"basic"` | No encoding costs and great for most use cases. Use `"plus"` if the user needs higher quality encoding |
| `static_renditions` | Do not set | Only set if the user explicitly needs downloadable MP4/M4A files. See the [static renditions guide](/docs/guides/enable-static-mp4-renditions) |
| `max_resolution_tier` | Do not set | Defaults to `1080p`. Set to `"2160p"` only if the user requests 4K |

### Cross-references for defaults

* `playback_policy`: `"public"` allows open access. Use `"signed"` for [secure video playback](/docs/guides/secure-video-playback) with [signed JWTs](/docs/guides/signing-jwts).
* `video_quality`: See [pricing](/docs/pricing/video) for the difference between `"basic"` and `"plus"`.
* `static_renditions`: Replaces the deprecated `mp4_support` parameter. Use `[{ "resolution": "highest" }]` for an MP4 download or `[{ "resolution": "audio-only" }]` for an M4A file. See the [static renditions guide](/docs/guides/enable-static-mp4-renditions) for all options.

## Persisting Mux data

After creating an asset, save the relevant data into your database or persistence layer. At minimum, store the **asset ID** and **playback ID**. You will also want to save metadata as it becomes available: `status`, `duration`, `aspect_ratio`, `resolution_tier`, and any `static_renditions` information.

For **simple integrations** with a fixed set of videos — hero videos, background videos, demo reels on a marketing site — you can hardcode playback IDs in a JSON file or config object. These rarely change and don't need a database.

For **production applications** where users upload videos, videos are created programmatically, or the video catalog changes over time, always persist asset data in a database. Use [webhooks](/docs/core/listen-for-webhooks) to keep your database in sync — listen for `video.asset.ready` to update status, and `video.asset.deleted` to clean up records.

## IDs reference

| ID type | What it's for |
| :--- | :--- |
| Asset ID | Managing the asset (get, update, delete) via `api.mux.com` |
| Playback ID | Streaming the video via `stream.mux.com` |
| Upload ID | Tracking direct upload status |
| Stream Key | Broadcasting to a live stream (keep secret) |
| Live Stream ID | Managing the live stream via `api.mux.com` |

## Common mistakes

**Do NOT confuse Asset IDs with Playback IDs.** Asset IDs are for API operations (`api.mux.com`). Playback IDs are for streaming (`stream.mux.com`). They are different strings.

**Do NOT use the playback URL before the asset is ready.** Always check `status === "ready"` first. A playback URL for a `preparing` asset will not work.

**Do NOT construct playback URLs with the Asset ID.** The correct URL is `https://stream.mux.com/{PLAYBACK_ID}.m3u8`, not `https://stream.mux.com/{ASSET_ID}.m3u8`.

**Do NOT expose API keys in client-side code.** API credentials (Token ID and Token Secret) must never be included in frontend JavaScript, mobile apps, or any code that runs on the user's device. All Mux API requests must be made from a trusted server.

**Do NOT expose stream keys in client-side code.** Stream keys allow anyone to broadcast to your live stream. Keep them server-side only.

**Do NOT hardcode playback URLs.** Always construct them from the playback ID returned by the API.

**Do NOT poll more than once per second.** The API has rate limits. Poll every 2 seconds for asset status.

**Do NOT use `POST` endpoints at high volume without backoff.** POST requests are rate limited to ~1 request per second sustained. GET requests allow ~5 per second.


# Make API requests
Learn how to work with Mux's API through HTTP requests.
## HTTP basic auth

| Term         | Description                                            |
| :----------- | :----------------------------------------------------- |
| Token ID     | access token ID, the "username" in HTTP basic auth     |
| Token secret | access token secret, the "password" in HTTP basic auth |

Every request to the API is authenticated via an [Access Token](https://dashboard.mux.com/settings/access-tokens), which includes the ID and the secret key. You can think of the Access Token’s ID as its username and secret as the password. Mux only stores a hash of the secret, not the secret itself. If you lose the secret key for your access token, Mux cannot recover it; you will have to create a new Access Token. If the secret key for an Access Token is leaked you should revoke that Access Token on the settings page: https://dashboard.mux.com/settings/access-tokens.

Note that in order to access the settings page for access tokens you must be an admin on the Mux organization.

API requests are authenticated via HTTP Basic Auth, where the username is the Access Token ID, and the password is the Access Token secret key. Due to the use of Basic Authentication and because doing so is just a Really Good Idea™, all API requests must made via HTTPS (to `https://api.mux.com`).

<Callout type="warning" title="Watch out for mismatched tokens and environments">
  Access tokens are scoped to an environment, for example: a development token cannot be used in requests to production. Verify the intended environment when creating an access token.
</Callout>

This is an example of authenticating a request with cURL, which automatically handles HTTP Basic Auth. If you run this request yourself it will not work, you should replace the Access Token ID (`44c819de-4add-4c9f-b2e9-384a0a71bede`) and secret (`INKxCoZ+cX6l1yrR6vqzYHVaeFEcqvZShznWM1U/No8KsV7h6Jxu1XXuTUQ91sdiGONK3H7NE7H`) in this example with your own credentials.

```shell
curl https://api.mux.com/video/v1/assets \
  -H "Content-Type: application/json" \
  -X POST \
  -d '{ "inputs": [{ "url": "https://muxed.s3.amazonaws.com/leds.mp4" }], "playback_policies": ["public"], "video_quality": "basic" }' \
  -u 44c819de-4add-4c9f-b2e9-384a0a71bede:INKxCoZ+cX6l1yrR6vqzYHVaeFEcqvZShznWM1U/No8KsV7h6Jxu1XXuTUQ91sdiGONK3H7NE7H
```

HTTP basic auth works by base64 encoding the username and password in an `Authorization` header on the request.

Specifically, the header looks something like this:

```bash
'Authorization': 'Basic base64(MUX_TOKEN_ID:MUX_TOKEN_SECRET)'
```

1. The access token ID and secret are concatenated with a `:` and the string is base64 encoded.
2. The value for the `Authorization` header is the string `Basic` plus a space ` ` followed by the base64 encoded result from Step 1.

In the cURL example above, the cURL library is taking care of the base64 encoding and setting the header value internally. The HTTP library you use in your server-side language will probably have something similar for handling basic auth. You should be able to pass in the `username` (Access Token ID) and `password` (Access Token secret) and the library will handle the details of formatting the header.

## Access token permissions

<Callout type="success" title="Full Permissions">
  If you're just getting started with Mux Video, use Read and Write.
</Callout>

If you are creating or modifying resources with Mux Video then you need **Read** and **Write** permissions. This includes things like:

* Creating new assets
* Creating direct uploads
* Creating new live streams

If you need to create signed tokens for secure video playback, your access token needs **System** write permissions. Learn more about [secure video playback](/docs/guides/secure-video-playback) and <ApiRefLink href="/docs/api-reference/system/signing-keys">signing keys</ApiRefLink>.

Mux Data only requires **Write** permissions if you need to create Annotations via API. Annotations created in the Dashboard do not require **Write** permissions.

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

If your code is not creating anything and only doing `GET` requests then you can restrict the access token to **Read** only.

## CORS and client side API requests

Mux API endpoints do not have CORS headers, which means if you try to call the Mux API from the browser you will get an error:

<Callout type="error" title="CORS Error in Browser">
  request has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
</Callout>

This is expected. Although making API requests directly from the browser or your mobile app would be convenient, it leaves a massive security hole in your application by the fact that your client side code would contain your API keys. Anyone who accesses your application would have the ability to steal your API credentials and make requests to Mux on your behalf. An attacker would be able to gain full control of your Mux account.

Mux API Credentials should never be stored in a client application. All Mux API calls should be made from a trusted server.

Instead of trying to make API requests from the client, the flow that your application should follow is:

1. Client makes a request to your server
2. Your server makes an authenticated API request to Mux
3. Your server saves whatever it needs in your database
4. Your server responds to the client with only the information that the client needs. For example, with live streaming that's the stream key for a specific stream, for uploads that's just the direct upload URL

## Using Mux with serverless functions

Serverless functions are a great way to add pieces of secure server-side code to your client heavy application. Examples of services that help you run serverless functions are:

* [AWS Lambda](https://aws.amazon.com/lambda/)
* [Firebase Cloud Functions](https://firebase.google.com/docs/functions)
* [Cloudflare Workers](https://workers.cloudflare.com/)
* [Vercel Functions](https://vercel.com/docs/functions)
* [Netlify Functions](https://docs.netlify.com/functions/overview/)

The basic idea behind serverless functions is that you can write a bit of server code and deploy it to run on these platforms. Your client application can make requests to these endpoints to perform specific actions. Below is an example from [with-mux-video](https://github.com/vercel/next.js/blob/canary/examples/with-mux-video/pages/api/upload.js) of a serverless function endpoint that makes an API call to create a Mux Direct Upload.

```js
// pages/api/upload.js
// see: https://github.com/vercel/next.js/tree/canary/examples/with-mux-video
import Mux from '@mux/mux-node';

const mux = new Mux();

export default async function uploadHandler(req, res) {
  const { method } = req;

  switch (method) {
    case 'POST':
      try {
        const upload = await mux.video.uploads.create({
          new_asset_settings: { playback_policy: ['public'], video_quality: 'basic' },
          cors_origin: '*',
        });
        res.json({
          id: upload.id,
          url: upload.url,
        });
      } catch (e) {
        console.error('Request error', e);
        res.status(500).json({ error: 'Error creating upload' });
      }
      break;
    default:
      res.setHeader('Allow', ['POST']);
      res.status(405).end(`Method ${method} Not Allowed`);
  }
}
```

## API pagination

Our list endpoints (such as <ApiRefLink href="/docs/api-reference/video/assets/list-assets">List Assets</ApiRefLink>) do not return every single relevant record.
To offer everyone the best performance we limit the amount of records you can receive and offer pagination parameters to help you navigate through your list.

### Page/limit pagination

Our most common pagination controls are `page` and `limit`.

| Parameter | Default | Maximum | Description                                      |
| :-------- | :------ | :---- | :--------------------------------------------------|
| `page`    | `1`     | None | The page number to return. The first page is `1`.   |
| `limit`   | `10`    | `100` | The number of records to return per page.          |

If you have 100 assets and you want to get the first 10, you would make a request like this:

```http
GET /video/v1/assets?page=1&limit=10
```

And if you want to get the next 10, you would increment the page parameter from `1` to `2` and make a request like this:

```http
GET /video/v1/assets?page=2&limit=10
```

### Cursor pagination

In addition to `page`/`limit`, the <ApiRefLink href="/docs/api-reference/video/assets/list-assets">List Assets</ApiRefLink> endpoint also supports cursor pagination.
Cursor pagination is a more efficient and reliable way of paginating through very large collections.

<Callout type="info" title="More to come">
  Cursor pagination is only available on the <ApiRefLink href="/docs/api-reference/video/assets/list-assets">List Assets</ApiRefLink> endpoint, but we plan to add it to more endpoints in the future. If you want it added to any specific endpoints please [let us know!](/support)
</Callout>

When you make a request to the list assets endpoint we return a `next_cursor` value.

```json
// GET /video/v1/assets
{
  "data": [
    {
      "id": "asset_id",
      "status": "ready",
      ...
    }
  ],
  "next_cursor": "eyJwYWdlX2xpbWl0IjoxMDAwLCJwYWdlX2NvdW50IjoxfQ"
}
```

Take that `next_cursor` value and make a new request to the list assets endpoint with the `cursor` parameter.

```json
// GET /video/v1/assets?cursor=eyJwYWdlX2xpbWl0IjoxMDAwLCJwYWdlX2NvdW50IjoxfQ
{
  "data": [
    {
      "id": "asset_id",
      "status": "ready",
      ...
    }
  ],
  "next_cursor": null
}
```

If `next_cursor` is `null`, you've reached the end of your list. If `next_cursor` is not `null` you can use that value to get the next page, repeating this pattern until `next_cursor` is `null`.

## API rate limits

Mux Video implements a simple set of rate limits. Rate limits are set per account (not per environment). These rate limits exist for two reasons:

1. First, to protect you, or customers from runaway scripts or batch process - we don't want you to accidentally delete all your content, or run up a large bill if you're not expecting it.
2. Second, to ensure that there's always Mux infrastructure available when our customers need it, for example to start that critical live stream, or ingest that urgent video.

<Callout type="warning" title="Exceeding the rate limit">
  When the rate limit threshold is exceeded, the API will return a HTTP status code `429`.
</Callout>

### Video API

1. All Video API activities that include a `POST` request to `https://api.mux.com/video/` are rate limited to a sustained 1 request per second (RPS) with the ability to burst above this for short periods of time. This includes creating new <ApiRefLink href="/docs/api-reference/video/assets">Assets</ApiRefLink>, <ApiRefLink href="/docs/api-reference/video/live-streams">Live Streams</ApiRefLink>, and <ApiRefLink href="/docs/api-reference/video/direct-uploads">Uploads</ApiRefLink>.

2. All other request methods are limited to 5 sustained requests per second (RPS) with the ability to burst above this for short periods of time. This includes `GET`, `PUT`, `PATCH`, & `DELETE` verbs. Examples include (but not limited to) requests for <ApiRefLink href="/docs/api-reference/video/assets/get-asset">retrieving an asset</ApiRefLink>, <ApiRefLink href="/docs/api-reference/video/assets/update-asset-mp4-support">updating mp4 support</ApiRefLink>, & <ApiRefLink href="/docs/api-reference/video/delivery-usage/list-delivery-usage">listing delivery usage</ApiRefLink>.

### Playback

There are no limits as to the number of viewers that your streams can have, all we ask is that you let us know if you're planning an event expected to receive more than 100,000 concurrent live viewers.

### Monitoring Data API

Requests against the <ApiRefLink href="/docs/api-reference/data/monitoring/list-monitoring-dimensions">Monitoring Data</ApiRefLink> APIs are rate limited to a sustained 1 request per second (RPS) with the ability to burst above this for short periods of time.

### General Data API

Requests against the all other <ApiRefLink href="/docs/api-reference/data/video-views">General Data</ApiRefLink> APIs are rate limited to a sustained 5 request per second (RPS) with the ability to burst above this for short periods of time.

# OpenAPI specification

The complete Mux API is described by an OpenAPI specification, available at [`https://www.mux.com/api-spec.json`](https://www.mux.com/api-spec.json). You can use this spec to generate API clients, import endpoints into tools like [Postman](/docs/core/postman), or integrate with any tooling that supports OpenAPI.


# Use a Mux SDK
Mux SDKs are available for a variety of languages and platforms.
Mux has API SDKs for several major languages. You are not required to use them, but these SDKs handle the details of authentication for you and make it a little nicer to send API requests to Mux; in languages with static typing or type hints, they also will help you form correct requests and reduce development time.

* [Node](/docs/integrations/mux-node-sdk)
* [Python](/docs/integrations/mux-python-sdk)
* [PHP](/docs/integrations/mux-php-sdk)
* [Ruby](/docs/integrations/mux-ruby-sdk)
* [Elixir](/docs/integrations/mux-elixir-sdk)
* [Java](/docs/integrations/mux-java-sdk)
* [C# and other .NET languages](/docs/integrations/mux-csharp-sdk)


# Make API requests with Postman
In this guide you will learn how to fork, set up, and work with Mux's API collection using Postman's API interface.
## Fork the collection

We recommend [Postman](https://postman.com) as a way to easily explore and interact with our API.

Similar to forking a repository on GitHub, forking a collection on Postman allows you to create a new instance of the collection.
Here, you can send requests, collaborate, and submit changes to the original collection.
Without forking the collection, the collection will be **read-only** and you will not be able to make requests unless you're a member of the workspace — even if the collection is public.

If you're already a Postman user, you can fork our [officially supported Postman collection](https://www.postman.com/muxinc/workspace/mux-apis/overview?utm_campaign=postman-collab\&utm_medium=guide\&utm_source=mux) and add it to your workspace by clicking the button below.

You can then stay up to date with future changes to our API specification by pulling changes. More on that in the sections below.

[![Run in Postman](https://run.pstmn.io/button.svg)](https://god.gw.postman.com/run-collection/18282356-97f1767e-f35a-4fca-b1c5-bf612e6f8e76?action=collection%2Ffork\&collection-url=entityId%3D18282356-97f1767e-f35a-4fca-b1c5-bf612e6f8e76%26entityType%3Dcollection%26workspaceId%3D2bcc854d-f831-4c9f-ac0a-3b4382f3a5cd)

## Basic authentication

| Term         | Description                                                |
| :----------- | :--------------------------------------------------------- |
| Token ID     | access token ID, the "username" in basic auth              |
| Token secret | access token secret key, the "password" in basic auth      |

## Set up credentials

Once you've created your access tokens via your [Mux account](https://dashboard.mux.com/signup?type=video?utm_campaign=postman-collab\&utm_medium=guide\&utm_source=mux), you can input them into their respective fields under authorization.

<Image src="/docs/images/postman-auth.png" width={1217} height={723} alt="Basic authentication in Postman" />

## Environment variables

You can use [environment variables](https://learning.postman.com/docs/sending-requests/variables/?utm_campaign=mux-collab\&utm_medium=site\&utm_source=mux) to store and reuse values — like your credentials —
across requests and collections. Variables can either be scoped to the environment or globally, available to all collections within a workspace.

To create environment variables, click the eye icon on the right-hand side of the collection and choose the scope you want your credentials to apply to.

<Image src="/docs/images/postman-env-variables.png" width={1217} height={723} alt="Environment variables menu in Postman" />

Next, add your credentials and set the type to **secret**. This will hide values on-screen. Once you've finished setting up your environment variables,
you can go back to basic authentication and use the variables instead of the values directly. To do this, use `{{variable_name}}` in the form field.

<Image src="/docs/images/postman-hidden-auth.png" width={1217} height={723} alt="Hidden authentication in Postman" />

## Sample request body and responses

Even with extensive documentation, it can be hard to navigate an API for the first time. To help you make requests and understand their responses, we use Postman's
[examples feature](https://learning.postman.com/docs/sending-requests/examples/?utm_campaign=mux-collab\&utm_medium=site\&utm_source=mux) for all Mux Video and Mux Data endpoints.

You can view an endpoint's sample request body by clicking the endpoint on the left-hand API menu and then clicking **body** in the main section of the interface.

<Image src="/docs/images/postman-sample-request-body.png" width={1217} height={723} alt="Sample API request body in Postman" />

You can view an endpoint's sample request response by clicking the right-facing carat on the endpoint. A new item will appear in the collection with the icon **e.g.**.

<Image src="/docs/images/postman-sample-request-response.png" width={1217} height={523} alt="Sample API request response in Postman" />

## Stay up to date with the main collection

Similar to a forked repository on GitHub, your Postman fork will only stay up to date with the origin collection if you periodically [pull changes](https://learning.postman.com/docs/collaborating-in-postman/version-control/#pulling-updates)
to keep your fork in sync.

You can pull changes by clicking the three dots next to the name of your fork. This will open a sub-menu. Click on **merge changes** near the bottom of the menu.

<Image src="/docs/images/postman-fork-sub-menu.png" width={517} height={123} alt="Forked Postman collection's sub-menu" />

If your fork is not in sync with the origin collection, there will be a yellow banner that states, "The destination has been modified since you last updated the fork. We’d recommend pulling changes." Click **pull changes** on the right.

You will then see a diff where source is the origin and destination is your fork.

<Image src="/docs/images/postman-pull-changes-diff.png" width={617} height={323} alt="API diff when pulling changes" />

Sometimes there will be merge conflicts. If you encounter them, you can choose whether you keep the source or destination version of a change.

Once everything looks good, click the orange button labeled **pull changes**.


# Listen for webhooks
Learn how to listen for webhooks from Mux.
Mux uses [webhooks](https://webhooks.fyi) to let your application know when things happen asynchronously, outside of an API request cycle. For example, you may want to update something on your end when an <ApiRefLink href="/docs/api-reference/video/assets/get-asset">asset</ApiRefLink> transitions its status from `processing` to `ready`, or when a live stream starts or ends. When these asynchronous events happen, we'll make a POST request to the address you give us and you can do whatever you need with it on your end.

After a webhook is configured for an environment, notifications will be sent for all events for that environment.

<Callout type="warning">
  Note that webhooks are scoped per *environment*. If you have configured webhooks and you are not seeing them show up, double check that the webhook is correctly configured for the environment you are working in.
</Callout>

If Mux doesn't receive a `2xx` response from your system, we will continue to try the message for the next 24 hours (with an increasing delay between attempts).

<Callout type="info">
  Mux makes an effort to deliver each message successfully once, but in certain
  situations duplicate webhook messages may be sent even if your service
  responds with a 2xx response code. Please ensure that your webhook handling
  mechanism treats duplicated event delivery appropriately.
</Callout>

# Webhooks vs. polling

Please use webhooks to track asset status rather than polling the <ApiRefLink href="/docs/api-reference/video/assets/get-asset">Asset API</ApiRefLink>. Webhooks are much more efficient for both you and Mux, and we rate limit GET requests to the `/assets` endpoint, which means polling the `/assets` API doesn't scale.

# Handling webhooks locally

A common gotcha for anyone new to working with webhooks is figuring out how to receive them when working in a local environment. Since your application runs on a local URL like `http://localhost:3000`, Mux can't reach it directly to deliver webhook events.

The recommended approach is to use the [Mux CLI](/docs/integrations/mux-cli) to listen for events and forward them to your local server.

## Using the Mux CLI

The Mux CLI can connect to Mux's event stream and forward webhook events to your local development server in real-time.

<Callout type="warning">
  CLI webhook forwarding is for **local development only** and provides **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>

### Listen and forward events

```bash
mux webhooks listen --forward-to http://localhost:3000/api/webhooks/mux
```

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.

### Replay past events

The CLI stores the last 100 events received during `listen` sessions. You can replay them to re-test your webhook handler without creating new resources:

```bash
# List stored events
mux webhooks events list

# Replay a specific event
mux webhooks events replay <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
```

### Trigger synthetic events

You can also send synthetic webhook events to your local server for testing, without making any API calls or creating real resources:

```bash
mux webhooks trigger video.asset.ready --forward-to http://localhost:3000/api/webhooks/mux
```

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

For the full list of webhook CLI commands, see the [Mux CLI docs](/docs/integrations/mux-cli#webhook-forwarding).

## Alternative: using ngrok

If you prefer, you can also use a tunneling tool like [ngrok](https://ngrok.com/docs/integrations/webhooks/mux-webhooks) to expose your local server to the internet and receive webhooks directly from Mux.

```bash
ngrok http 3000
```

This gives you a public URL (e.g. `https://abc123.ngrok.io`) that you can configure as a webhook endpoint in the [Mux Dashboard](https://dashboard.mux.com). Your full webhook URL would be something like `https://abc123.ngrok.io/api/webhooks/mux`.

<Callout type="info">
  You'll need to create an ngrok account (a free account works for most testing purposes). See [ngrok's Mux integration docs](https://ngrok.com/docs/integrations/webhooks/mux-webhooks) for more details.
</Callout>

# Configuring endpoints

Webhook endpoints are configured in the Mux dashboard under "Settings."

<Image src="/docs/images/webhooks.png" width={500} height={500} />

Enter a URL from your application that Mux will call for event notifications.

<Image src="/docs/images/new-webhook.png" width={1192} height={898} />

# Receiving events

Mux will submit a POST request to the configured URL, which your application can treat the same as any other route. Your event handler can do things like update the state of the specified asset in your database, or trigger other work.

Note that a single request attempt will timeout after 5 seconds, after which the attempt is considered failed and will be reattempted. If you expect this will be a problem in your workflow, consider doing the work in an asynchronous task so you can respond to the event immediately.

For more details on the Webhook event object definition, see [the example response](#example-response).

# Example response

```json
{
  "type": "video.asset.ready",
  "object": {
    "type": "asset",
    "id": "0201p02fGKPE7MrbC269XRD7LpcHhrmbu0002"
  },
  "id": "3a56ac3d-33da-4366-855b-f592d898409d",
  "environment": {
    "name": "Demo pages",
    "id": "j0863n"
  },
  "data": {
    "tracks": [
      {
        "type": "video",
        "max_width": 1280,
        "max_height": 544,
        "max_frame_rate": 23.976,
        "id": "0201p02fGKPE7MrbC269XRD7LpcHhrmbu0002",
        "duration": 153.361542
      },
      {
        "type": "audio",
        "max_channels": 2,
        "max_channel_layout": "stereo",
        "id": "FzB95vBizv02bYNqO5QVzNWRrVo5SnQju",
        "duration": 153.361497
      }
    ],
    "status": "ready",
    "max_stored_resolution": "SD",
    "max_stored_frame_rate": 23.976,
    "id": "0201p02fGKPE7MrbC269XRD7LpcHhrmbu0002",
    "duration": 153.361542,
    "created_at": "2018-02-15T01:04:45.000Z",
    "aspect_ratio": "40:17"
  },
  "created_at": "2018-02-15T01:04:45.000Z",
  "accessor_source": null,
  "accessor": null,
  "request_id": null
}
```

# Types of Events

## Asset Events

| Event | Description |
|-------|-------------|
| `video.asset.created` | Asset has been created |
| `video.asset.ready` | Asset is ready for playback. You can now use the asset's `playback_id` to successfully start streaming this asset. |
| `video.asset.errored` | Asset has encountered an error. Use this to notify your server about assets with errors. Asset errors can happen for a number of reasons, most commonly an input URL that Mux is unable to download or a file that is not a valid video file. |
| `video.asset.updated` | Asset has been updated. Use this to make sure your server is notified about changes to assets. |
| `video.asset.deleted` | Asset has been deleted. Use this so that your server knows when an asset has been deleted, at which point it will no longer be playable. |
| `video.asset.live_stream_completed` | The live stream for this asset has completed. Every time a live stream starts and ends a new asset gets created and this event fires. |
| `video.asset.static_rendition.created` | A new static rendition for this asset has been created. Static renditions are streamable mp4 files that are most commonly used for allowing users to download files for offline viewing. |
| `video.asset.static_rendition.ready` | A static rendition for this asset is ready. Static renditions are streamable mp4 files that are most commonly used for allowing users to download files for offline viewing. |
| `video.asset.static_rendition.skipped` | A static rendition for this asset was skipped, due to the source not being suitable for the requested static rendition. Static renditions are streamable mp4 files that are most commonly used for allowing users to download files for offline viewing. |
| `video.asset.static_rendition.deleted` | A static rendition for this asset was deleted. The static renditions (mp4 files) for this asset will no longer be available. |
| `video.asset.static_rendition.errored` | A static rendition for this asset errored. This indicates that there was some error when creating a static rendition (mp4s) of your asset. This should be rare and if you see it unexpectedly please open a support ticket. |
| `video.asset.master.ready` | Master access for this asset is ready. Master access is used when downloading an asset for purposes of editing or post-production work. The master access file is not intended to be streamed or downloaded by end-users. |
| `video.asset.master.preparing` | Master access for this asset is being prepared. After requesting master access you will get this webhook while it is being prepared. |
| `video.asset.master.deleted` | Master access for this asset has been deleted. Master access for this asset has been removed. You will no longer be able to download the master file. If you want it again you should re-request it. |
| `video.asset.master.errored` | Master access for this asset has encountered an error. This indicates that there was some error when creating master access for this asset. This should be rare and if you see it unexpectedly please open a support ticket. |
| `video.asset.track.created` | A new track for this asset has been created, for example a subtitle text track. |
| `video.asset.track.ready` | A track for this asset is ready. In the example of a subtitle text track the text track will now be delivered with your HLS stream. |
| `video.asset.track.errored` | A track for this asset has encountered an error. There was some error preparing this track. Most commonly this could be a text track file that Mux was unable to download for processing. |
| `video.asset.track.deleted` | A track for this asset has been deleted. |
| `video.asset.warning` | This event fires when Mux has encountered a non-fatal issue with the recorded asset of the live stream. At this time, the event is only fired when Mux is unable to download a slate image from the URL set as `reconnect_slate_url` parameter value. More details on this event is available [here](/docs/guides/handle-live-stream-disconnects#reconnect-window-and-slates). |

## Upload Events

| Event | Description |
|-------|-------------|
| `video.upload.asset_created` | An asset has been created from this upload. This is useful to know what a user of your application has finished uploading a file using the URL created by a [Direct Upload](/docs/guides/upload-files-directly). |
| `video.upload.cancelled` | Upload has been canceled. This event fires after hitting the <ApiRefLink href="/docs/api-reference/video/direct-uploads/cancel-direct-upload">cancel direct upload</ApiRefLink> API. |
| `video.upload.created` | Upload has been created. This event fires after <ApiRefLink href="/docs/api-reference/video/direct-uploads/create-direct-upload">creating a direct upload</ApiRefLink>. |
| `video.upload.errored` | Upload has encountered an error. This event fires when the asset created by the direct upload fails. Most commonly this happens when an end-user uploads a non-video file. |

## Live Stream Events

| Event | Description |
|-------|-------------|
| `video.live_stream.created` | A new live stream has been created. Broadcasters with a `stream_key` can start sending encoder feed to this live stream. |
| `video.live_stream.connected` | An encoder has successfully connected to this live stream. |
| `video.live_stream.recording` | Recording on this live stream has started. Mux has successfully processed the first frames from the encoder. If you show a *red dot* icon in your UI, this would be a good time to show it. |
| `video.live_stream.active` | This live stream is now "active". The live streams `playback_id` OR the `playback_id` associated with this live stream's asset can be used right now to created HLS URLs (`https://stream.mux.com/{PLAYBACK_ID}.m3u8` and start streaming in your player. Note that before the live stream is `"active"`, trying to stream the HLS URL will result in HTTP `412` errors. |
| `video.live_stream.disconnected` | An encoder has disconnected from this live stream. Note that while disconnected the live stream is still `status: "active"`. |
| `video.live_stream.idle` | The `reconnect_window` for this live stream has elapsed. The live stream `status` will now transition to `"idle"`. |
| `video.live_stream.updated` | This live stream has been updated. For example, after <ApiRefLink href="/docs/api-reference/video/live-streams/reset-stream-key">resetting the live stream's stream key</ApiRefLink>. |
| `video.live_stream.enabled` | This live stream has been enabled. This event fires after <ApiRefLink href="/docs/api-reference/video/live-streams/enable-live-stream">enable live stream</ApiRefLink> API. |
| `video.live_stream.disabled` | This live stream has been disabled. This event fires after <ApiRefLink href="/docs/api-reference/video/live-streams/disable-live-stream">disable live stream</ApiRefLink> API. Disabled live streams will no longer accept new RTMP connections. |
| `video.live_stream.deleted` | This live stream has been deleted. This event fires after <ApiRefLink href="/docs/api-reference/video/live-streams/delete-live-stream">delete live stream API</ApiRefLink> API. |
| `video.live_stream.warning` | This live stream event fires when Mux has encountered a non-fatal issue. There is no disruption to the live stream ingest and playback. At this time, the event is only fired when Mux is unable to download an image from the URL set as `reconnect_slate_url` parameter value. More details on this event is available [here](/docs/guides/handle-live-stream-disconnects#reconnect-window-and-slates). |

## Simulcast Target Events

These simulcast target events are useful when creating a UI that shows your users the status of their configured 3rd party endpoints. These events are handy when you want to build a UI that shows the state of each simulcast target and keep track of the state changes as they happen.

| Event | Description |
|-------|-------------|
| `video.live_stream.simulcast_target.created` | A new simulcast target has been created for this live stream. |
| `video.live_stream.simulcast_target.idle` | When the parent live stream is `"disconnected"`, all simulcast targets will have be `"idle"`. |
| `video.live_stream.simulcast_target.starting` | When the parent live stream fires `"connected"` then the simulcast targets transition to `"starting"`. |
| `video.live_stream.simulcast_target.broadcasting` | This fires when Mux has successfully connected to the simulcast target and has begun pushing content to that third party. |
| `video.live_stream.simulcast_target.errored` | This fires when Mux has encountered an error either while attempting to connect to the third party streaming service or while broadcasting. Mux will try to re-establish the connection and if it does successfully the simulcast target will transition back to `"broadcasting"`. |
| `video.live_stream.simulcast_target.updated` | This simulcast target has been updated. |
| `video.live_stream.simulcast_target.deleted` | This simulcast target has been deleted. |

# Webhook specification

A machine-readable specification of all Mux webhook events is available at [`https://www.mux.com/webhook-spec.json`](https://www.mux.com/webhook-spec.json). You can use this to generate types, validate payloads, or integrate with any tooling that supports OpenAPI-style schemas.


# Verify webhook signatures
You have the option to verify webhook requests that Mux sends to your endpoints. Mux will include a signature in the request's header. You can use this signature in your code to make sure the request was sent by Mux and not a third party.
## Obtain your signing secret

Before you get started, you will need your signing secret for your webhook. You can find that where you configure webhooks on the [webhooks settings page](https://dashboard.mux.com/settings/webhooks). Please note that the signing secret is different for each webhook endpoint that we notify.

<Image src="/docs/images/webhook-security.png" width={1181} height={479} />

Webhooks contain a header called `mux-signature` with the timestamp and a signature. The timestamp is prefixed by `t=` and the signature is prefixed by a scheme. Schemes start with `v`, followed by an integer. Currently, the only valid signature scheme is `v1`. Mux generates signatures using [HMAC](https://en.wikipedia.org/wiki/HMAC) with [SHA-256](https://en.wikipedia.org/wiki/SHA-2).

```text
Mux-Signature: t=1565220904,v1=20c75c1180c701ee8a796e81507cfd5c932fc17cf63a4a55566fd38da3a2d3d2`
```

## How to verify webhook signatures

### Step 1: Extract the timestamp and signature

Split the header at the `,` character and get the values for `t` (timestamp) and `v1` (the signature)

### Step 2: Prepare the `signed_payload` string

You will need:

* the timestamp from Step 1 as a string (for example: "1565220904")
* the dot character `.`
* the raw request body (this will be JSON in a string format)

### Step 3: Determine the expected signature

Use the 3 components from Step 2 to compute an HMAC with the SHA256 hash function. Depending on the language that you are using this will look something like the following:

```js
secret = 'my secret' // your signing secret
payload = timestamp + "." + request_body
expected_signature = createHmacSha256(payload, secret)
```

### Step 4: Compare signature

Compare the signature in the header to the expected signature. If the signature matches, compute the difference between the current timestamp and the received timestamp, then check to make sure that the timestamp is within our tolerance. By default, our SDKs allow a tolerance of 5 minutes.

## Examples

Our official SDKs for [Node](https://github.com/muxinc/mux-node-sdk) and [Elixir](https://github.com/muxinc/mux-elixir) contain helper methods for verifying Mux webhooks. If you're using one of these languages it's best to use our available helper methods. Note that the helper methods use the raw request body instead of a payload including the timestamp.

```elixir

# check the mux-elixr docs for details and a full example using Phoenix
# https://github.com/muxinc/mux-elixir#verifying-webhook-signatures-in-phoenix
Mux.Webhooks.verify_header(raw_body, signature_header, secret)

```

```go

func generateHmacSignature(webhookSecret, payload string) string {
    h := hmac.New(sha256.New, []byte(webhookSecret))
    h.Write([]byte(payload))
    return hex.EncodeToString(h.Sum(nil))
}

func IsValidMuxSignature(req *http.Request, body []byte) error {
    muxSignature := req.Header.Get("Mux-Signature")

    if muxSignature == "" {
        return errors.New("no Mux-Signature in request header")
    }

    muxSignatureArr := strings.Split(muxSignature, ",")

    if len(muxSignatureArr) != 2 {
        return errors.New(fmt.Sprintf("Mux-Signature in request header should be 2 values long: %s", muxSignatureArr))
    }

    timestampArr := strings.Split(muxSignatureArr[0], "=")
    v1SignatureArr := strings.Split(muxSignatureArr[1], "=")

    if len(timestampArr) != 2 || len(v1SignatureArr) != 2 {
        return errors.New(fmt.Sprintf("missing timestamp: %s or missing v1Signature: %s", timestampArr, v1SignatureArr))
    }

    timestamp := timestampArr[1]
    v1Signature := v1SignatureArr[1]

    webhookSecret := "" //insert secret here or load from config file.
    payload := fmt.Sprintf("%s.%s", timestamp, string(body))
    sha := generateHmacSignature(webhookSecret, payload)

    if sha != v1Signature {
        return errors.New("not a valid mux webhook signature")
    }

    fmt.Println("timestamp sha:", sha)
    fmt.Println("v1Signature:", v1Signature)
    return nil
}

```

```laravel

/**
 * Verify the signature (laravel)
 *
 * @param Request $request
 * @return boolean
 */
protected function verifySignature(Request $request)
{
    // Get the signature from the request header
    $muxSig = $request->header('Mux-Signature');

    if(empty($muxSig)) {
        return false;
    }

    // Split the signature based on ','.
    // Format is 't=[timestamp],v1=[hash]'
    $muxSigArray = explode(',', $muxSig);

    if(empty($muxSigArray) || empty($muxSigArray[0]) || empty($muxSigArray[1])) {
        return false;
    }

    // Strip the first occurence of 't=' and 'v1=' from both strings
    $muxTimestamp = Str::replaceFirst('t=', '', $muxSigArray[0]);
    $muxHash = Str::replaceFirst('v1=', '', $muxSigArray[1]);

    // Create a payload of the timestamp from the Mux signature and the request body with a '.' in-between
    $payload = $muxTimestamp . "." . $request->getContent();

    // Build a HMAC hash using SHA256 algo, using our webhook secret
    $ourSignature = hash_hmac('sha256', $payload, config('mux.webhook_secret'));

    // `hash_equals` performs a timing-safe crypto comparison
    return hash_equals($ourSignature, $muxHash);
}

```

```node

import Mux from '@mux/mux-node';

// check the mux-node-sdk docs for details
// https://github.com/muxinc/mux-node-sdk/blob/master/api.md#webhooks
const mux = new Mux();
mux.webhooks.verifySignature(body, headers, secret);

```



# Content Security Policy for Mux
Learn how to configure Content Security Policy (CSP) to work with Mux Video and Data services.
## Understanding CSP with Mux

Content Security Policy (CSP) is a security feature that helps protect your web application from cross-site scripting (XSS) attacks and other code injection attacks. CSP works by restricting the resources (such as scripts, stylesheets, images, and network connections) that a web page can load.

When integrating Mux Video and Mux Data into your application, you'll need to configure your CSP to allow connections to Mux services. This guide will help you set up the appropriate CSP directives to ensure your Mux integration works securely.

<Callout type="info" title="CSP Basics">
  If you're new to Content Security Policy, we recommend reading [Google's CSP guide](https://developers.google.com/web/fundamentals/security/csp) for a comprehensive introduction to CSP concepts and implementation.
</Callout>

## Basic CSP configuration

For most applications, the simplest approach is to use a basic CSP that allows all Mux services. This configuration ensures compatibility with all current and future Mux features:

```
Content-Security-Policy: default-src 'self' *.mux.com *.litix.io storage.googleapis.com
```

This CSP directive allows your application to:

* Load resources from your own domain (`'self'`)
* Connect to all Mux Video services (`*.mux.com`)
* Connect to all Mux Data services (`*.litix.io`)
* Connect to Google Cloud Storage (`storage.googleapis.com`) -- this is needed for [Direct Uploads](/docs/guides/upload-files-directly)

The wildcard approach for `mux.com` and `litix.io` is recommended because Mux utilizes multiple CDNs and subdomains to provide optimal performance globally. These hostnames may change without notice as we optimize our infrastructure.

## Granular CSP configuration

If your security requirements call for a more restrictive CSP, you can use specific directives instead of the broad `default-src` approach. Here's a granular configuration that covers all Mux functionality:

```
Content-Security-Policy: 
  connect-src 'self' https://*.mux.com https://*.litix.io https://storage.googleapis.com;
  media-src 'self' blob: https://*.mux.com;
  img-src 'self' https://image.mux.com https://*.litix.io;
  script-src 'self' https://src.litix.io;
  worker-src 'self' blob:
```

<Callout type="warning" title="Merge with existing policies">
  The above configuration must be merged with your existing CSP directives. Each directive should combine values from both your current policy and the Mux requirements.
</Callout>

## Upload and media handling

If your application uploads media files to Mux via [Direct Uploads](/docs/guides/upload-files-directly), you'll need additional CSP directives to handle binary data and file uploads:

```
Content-Security-Policy: 
  connect-src 'self' https://*.mux.com https://*.litix.io https://storage.googleapis.com;
  media-src 'self' blob: https://*.mux.com;
  img-src 'self' https://image.mux.com https://*.litix.io;
  script-src 'self' https://src.litix.io;
  worker-src 'self' blob:;
  form-action 'self' https://*.mux.com https://storage.googleapis.com
```

The key additions for upload functionality are:

| Directive | Purpose |
| :-------- | :------ |
| `https://storage.googleapis.com` in `connect-src` | Allows uploads to Google Cloud Storage endpoints used by Mux |
| `form-action` directive | Permits form submissions and PUT/POST requests to upload endpoints |
| `blob:` in `media-src` and `worker-src` | Enables handling of binary file data during upload processing |

## Product-specific requirements

Different Mux features have specific CSP requirements. Here's what you need for each:

### Mux Video Playback

For video playback functionality, you **must** include:

```
connect-src https://*.mux.com;
media-src blob: https://*.mux.com;
worker-src blob:
```

This is required because:

* HLS manifests and video segments are delivered via `https://stream.mux.com` and other `*.mux.com` subdomains
* Video players use web workers and blob URLs for optimal performance
* Mux uses multiple CDNs with different hostnames for global performance

### Video Thumbnails and Storyboards

If you're displaying video thumbnails or timeline hover previews, include:

```
img-src https://image.mux.com;
connect-src https://image.mux.com
```

The `connect-src` directive is needed for dynamic thumbnail loading in timeline hover previews, while `img-src` covers standard image embedding.

### Mux Data Integration

For Mux Data analytics, you **must** allow:

```
connect-src https://*.litix.io;
img-src https://*.litix.io
```

This covers:

* Data collection endpoints across multiple subdomains
* Fallback beacon loading through image tags
* Various monitoring and analytics endpoints

<Callout type="success" title="Environment-specific restriction">
  For tighter security, you can replace `https://*.litix.io` with `https://img.litix.io` and `https://<env_key>.litix.io` where `<env_key>` is your Mux environment key. However, the wildcard approach is recommended for maximum compatibility.
</Callout>

### Hosted Mux Data Integrations

If you're loading pre-built Mux Data integrations from our hosted domain (rather than installing via NPM), add:

```
script-src https://src.litix.io
```

This is not required if you bundle the Mux Data SDK directly into your application code.

### Complete Example

Here's a complete CSP that supports all Mux features including uploads:

```
Content-Security-Policy: 
  default-src 'self';
  connect-src 'self' https://*.mux.com https://*.litix.io https://storage.googleapis.com;
  media-src 'self' blob: https://*.mux.com;
  img-src 'self' https://image.mux.com https://*.litix.io;
  script-src 'self' https://src.litix.io;
  worker-src 'self' blob:;
  form-action 'self' https://*.mux.com https://storage.googleapis.com
```

<Callout type="warning" title="Test thoroughly">
  After implementing your CSP, test all Mux functionality in your application including video playback, uploads, thumbnails, and analytics to ensure everything works as expected.
</Callout>
