Official TypeScript library providing comprehensive client access to Mux's video infrastructure API including asset management, live streaming, analytics, and webhook handling
Webhook signature verification and event parsing for secure handling of Mux platform events including video processing, live streaming, and system notifications.
import { Mux } from "@mux/mux-node";
// For webhook handling
const mux = new Mux({
webhookSecret: process.env.MUX_WEBHOOK_SECRET
});
// Access webhook utilities
const webhooks = mux.webhooks;Verify webhook signatures and parse webhook events with automatic type detection and validation.
/**
* Verify webhook signature and parse event payload
* @param body - Raw webhook request body (string)
* @param headers - HTTP headers from webhook request
* @param secret - Webhook signing secret (optional, uses client config if not provided)
* @returns Parsed and verified webhook event
* @throws Error if signature verification fails
*/
unwrap(body: string, headers: HeadersLike, secret?: string): UnwrapWebhookEvent;
/**
* Verify webhook signature without parsing event
* @param body - Raw webhook request body (string)
* @param headers - HTTP headers from webhook request
* @param secret - Webhook signing secret (optional, uses client config if not provided)
* @throws Error if signature verification fails
*/
verifySignature(body: string, headers: HeadersLike, secret?: string): void;
interface HeadersProtocol {
get: (header: string) => string | null | undefined;
}
type HeadersLike = Record<string, string | string[] | undefined> | HeadersProtocol;Usage Examples:
import express from 'express';
const app = express();
// Webhook endpoint with raw body parsing
app.post('/webhooks/mux', express.raw({ type: 'application/json' }), (req, res) => {
try {
// Verify and parse webhook event
const event = mux.webhooks.unwrap(
req.body.toString(),
req.headers,
process.env.MUX_WEBHOOK_SECRET
);
// Handle different event types
switch (event.type) {
case 'video.asset.ready':
console.log('Asset ready:', event.data.id);
break;
case 'video.live_stream.active':
console.log('Live stream active:', event.data.id);
break;
default:
console.log('Unknown event type:', event.type);
}
res.status(200).send('OK');
} catch (error) {
console.error('Webhook verification failed:', error);
res.status(400).send('Invalid signature');
}
});
// Signature verification only
app.post('/webhooks/verify-only', express.raw({ type: 'application/json' }), (req, res) => {
try {
mux.webhooks.verifySignature(req.body.toString(), req.headers);
// Signature is valid, process the raw body as needed
const eventData = JSON.parse(req.body.toString());
console.log('Valid webhook received:', eventData.type);
res.status(200).send('OK');
} catch (error) {
res.status(400).send('Invalid signature');
}
});Events related to video asset processing and lifecycle.
interface VideoAssetCreatedWebhookEvent extends BaseWebhookEvent {
type: 'video.asset.created';
data: Asset;
}
interface VideoAssetReadyWebhookEvent extends BaseWebhookEvent {
type: 'video.asset.ready';
data: Asset;
}
interface VideoAssetErroredWebhookEvent extends BaseWebhookEvent {
type: 'video.asset.errored';
data: Asset;
}
interface VideoAssetUpdatedWebhookEvent extends BaseWebhookEvent {
type: 'video.asset.updated';
data: Asset;
}
interface VideoAssetDeletedWebhookEvent extends BaseWebhookEvent {
type: 'video.asset.deleted';
data: Asset;
}
interface VideoAssetWarningWebhookEvent extends BaseWebhookEvent {
type: 'video.asset.warning';
data: Asset & {
/** Warning message */
warning?: string;
};
}
interface VideoAssetNonStandardInputDetectedWebhookEvent extends BaseWebhookEvent {
type: 'video.asset.non_standard_input_detected';
data: Asset;
}
interface VideoAssetLiveStreamCompletedWebhookEvent extends BaseWebhookEvent {
type: 'video.asset.live_stream_completed';
data: Asset;
}
interface VideoAssetStaticRenditionsReadyWebhookEvent extends BaseWebhookEvent {
type: 'video.asset.static_renditions.ready';
data: Asset;
}
interface VideoAssetStaticRenditionsPreparingWebhookEvent extends BaseWebhookEvent {
type: 'video.asset.static_renditions.preparing';
data: Asset;
}
interface VideoAssetStaticRenditionsDeletedWebhookEvent extends BaseWebhookEvent {
type: 'video.asset.static_renditions.deleted';
data: Asset;
}
interface VideoAssetStaticRenditionsErroredWebhookEvent extends BaseWebhookEvent {
type: 'video.asset.static_renditions.errored';
data: Asset;
}
interface VideoAssetMasterReadyWebhookEvent extends BaseWebhookEvent {
type: 'video.asset.master.ready';
data: Asset;
}
interface VideoAssetMasterPreparingWebhookEvent extends BaseWebhookEvent {
type: 'video.asset.master.preparing';
data: Asset;
}
interface VideoAssetMasterErroredWebhookEvent extends BaseWebhookEvent {
type: 'video.asset.master.errored';
data: Asset;
}
interface VideoAssetMasterDeletedWebhookEvent extends BaseWebhookEvent {
type: 'video.asset.master.deleted';
data: Asset;
}Events related to live streaming operations and status changes.
interface VideoLiveStreamCreatedWebhookEvent extends BaseWebhookEvent {
type: 'video.live_stream.created';
data: LiveStream;
}
interface VideoLiveStreamConnectedWebhookEvent extends BaseWebhookEvent {
type: 'video.live_stream.connected';
data: LiveStream;
}
interface VideoLiveStreamActiveWebhookEvent extends BaseWebhookEvent {
type: 'video.live_stream.active';
data: LiveStream;
}
interface VideoLiveStreamIdleWebhookEvent extends BaseWebhookEvent {
type: 'video.live_stream.idle';
data: LiveStream;
}
interface VideoLiveStreamDisconnectedWebhookEvent extends BaseWebhookEvent {
type: 'video.live_stream.disconnected';
data: LiveStream;
}
interface VideoLiveStreamUpdatedWebhookEvent extends BaseWebhookEvent {
type: 'video.live_stream.updated';
data: LiveStream;
}
interface VideoLiveStreamEnabledWebhookEvent extends BaseWebhookEvent {
type: 'video.live_stream.enabled';
data: LiveStream;
}
interface VideoLiveStreamDisabledWebhookEvent extends BaseWebhookEvent {
type: 'video.live_stream.disabled';
data: LiveStream;
}
interface VideoLiveStreamDeletedWebhookEvent extends BaseWebhookEvent {
type: 'video.live_stream.deleted';
data: LiveStream;
}
interface VideoLiveStreamWarningWebhookEvent extends BaseWebhookEvent {
type: 'video.live_stream.warning';
data: LiveStream & {
/** Warning message */
warning?: string;
};
}
interface VideoLiveStreamRecordingWebhookEvent extends BaseWebhookEvent {
type: 'video.live_stream.recording';
data: LiveStream & { recording_start_time?: string };
}Events related to direct upload operations and status.
interface VideoUploadCreatedWebhookEvent extends BaseWebhookEvent {
type: 'video.upload.created';
data: Upload;
}
interface VideoUploadCancelledWebhookEvent extends BaseWebhookEvent {
type: 'video.upload.cancelled';
data: Upload;
}
interface VideoUploadErroredWebhookEvent extends BaseWebhookEvent {
type: 'video.upload.errored';
data: Upload;
}
interface VideoUploadAssetCreatedWebhookEvent extends BaseWebhookEvent {
type: 'video.upload.asset_created';
data: Upload;
}Events for MP4 rendition generation and processing.
interface VideoAssetStaticRenditionCreatedWebhookEvent extends BaseWebhookEvent {
type: 'video.asset.static_rendition.created';
data: StaticRendition;
}
interface VideoAssetStaticRenditionReadyWebhookEvent extends BaseWebhookEvent {
type: 'video.asset.static_rendition.ready';
data: StaticRendition;
}
interface VideoAssetStaticRenditionErroredWebhookEvent extends BaseWebhookEvent {
type: 'video.asset.static_rendition.errored';
data: StaticRendition;
}
interface VideoAssetStaticRenditionDeletedWebhookEvent extends BaseWebhookEvent {
type: 'video.asset.static_rendition.deleted';
data: StaticRendition;
}
interface VideoAssetStaticRenditionSkippedWebhookEvent extends BaseWebhookEvent {
type: 'video.asset.static_rendition.skipped';
data: StaticRendition;
}Events for subtitle and audio track operations.
interface VideoAssetTrackCreatedWebhookEvent extends BaseWebhookEvent {
type: 'video.asset.track.created';
data: Track;
}
interface VideoAssetTrackReadyWebhookEvent extends BaseWebhookEvent {
type: 'video.asset.track.ready';
data: Track;
}
interface VideoAssetTrackErroredWebhookEvent extends BaseWebhookEvent {
type: 'video.asset.track.errored';
data: Track;
}
interface VideoAssetTrackDeletedWebhookEvent extends BaseWebhookEvent {
type: 'video.asset.track.deleted';
data: Track;
}Events for simulcast target operations during live streaming.
interface VideoLiveStreamSimulcastTargetCreatedWebhookEvent extends BaseWebhookEvent {
type: 'video.live_stream.simulcast_target.created';
data: SimulcastTarget;
}
interface VideoLiveStreamSimulcastTargetIdleWebhookEvent extends BaseWebhookEvent {
type: 'video.live_stream.simulcast_target.idle';
data: SimulcastTarget;
}
interface VideoLiveStreamSimulcastTargetStartingWebhookEvent extends BaseWebhookEvent {
type: 'video.live_stream.simulcast_target.starting';
data: SimulcastTarget;
}
interface VideoLiveStreamSimulcastTargetBroadcastingWebhookEvent extends BaseWebhookEvent {
type: 'video.live_stream.simulcast_target.broadcasting';
data: SimulcastTarget;
}
interface VideoLiveStreamSimulcastTargetErroredWebhookEvent extends BaseWebhookEvent {
type: 'video.live_stream.simulcast_target.errored';
data: SimulcastTarget;
}
interface VideoLiveStreamSimulcastTargetDeletedWebhookEvent extends BaseWebhookEvent {
type: 'video.live_stream.simulcast_target.deleted';
data: SimulcastTarget;
}
interface VideoLiveStreamSimulcastTargetUpdatedWebhookEvent extends BaseWebhookEvent {
type: 'video.live_stream.simulcast_target.updated';
data: SimulcastTarget;
}Events for system-level notifications and delivery monitoring.
interface VideoDeliveryHighTrafficWebhookEvent extends BaseWebhookEvent {
type: 'video.delivery.high_traffic';
data: {
/** Unique identifier for the delivery report */
id?: string;
/** Array of asset delivery data */
data?: Array<AssetDeliveryData>;
/** Current threshold set for alerting */
threshold?: number;
/** Time range for the report as [start, end] timestamps */
timeframe?: Array<number>;
};
}
interface AssetDeliveryData {
/** The duration of the asset in seconds */
asset_duration: number;
/** @deprecated Use asset_video_quality instead. The encoding tier that the asset was ingested at */
asset_encoding_tier: 'smart' | 'baseline' | 'premium';
/** Unique identifier for the asset */
asset_id: string;
/** The resolution tier that the asset was ingested at */
asset_resolution_tier: 'audio-only' | '720p' | '1080p' | '1440p' | '2160p';
/** The state of the asset */
asset_state: 'ready' | 'errored' | 'deleted';
/** Time at which the asset was created. Measured in seconds since the Unix epoch */
created_at: number;
/** Total number of delivered seconds during this time window */
delivered_seconds: number;
/** Seconds delivered broken into resolution tiers */
delivered_seconds_by_resolution: DeliveredSecondsByResolution;
/** The video quality that the asset was ingested at (replaces asset_encoding_tier) */
asset_video_quality?: 'basic' | 'plus' | 'premium';
/** If exists, time at which the asset was deleted. Measured in seconds since the Unix epoch */
deleted_at?: number;
/** Unique identifier for the live stream that created the asset */
live_stream_id?: string;
/** The passthrough value for the asset */
passthrough?: string;
}
interface DeliveredSecondsByResolution {
/** Delivered seconds within the 720p tier (up to 921,600 pixels total) */
tier_720p?: number;
/** Delivered seconds in 1080p tier (over 921,600 and <= 2,073,600 pixels) */
tier_1080p?: number;
/** Delivered seconds in 1440p tier (over 2,073,600 and <= 4,194,304 pixels) */
tier_1440p?: number;
/** Delivered seconds in 2160p tier (over 4,194,304 pixels) */
tier_2160p?: number;
/** Delivered seconds of audio only content */
tier_audio_only?: number;
}All webhook events extend the base webhook event interface:
interface BaseWebhookEvent {
/** Unique identifier for the event */
id: string;
/** Event type identifier */
type: string;
/** Event creation timestamp */
created_at: string;
/** Event data payload */
data: unknown;
/** Attempts for sending out the webhook event */
attempts: Array<WebhookAttempt>;
/** Environment information */
environment: WebhookEnvironment;
/** Object metadata */
object: BaseWebhookEvent.Object;
/** @deprecated Request ID that triggered the event */
request_id?: string | null;
/** @deprecated Accessor information */
accessor?: string | null;
/** @deprecated Accessor source */
accessor_source?: string | null;
}
namespace BaseWebhookEvent {
interface Object {
/** Object identifier */
id: string;
/** Object type */
type: string;
}
}
interface WebhookAttempt {
/** Unique identifier for the webhook attempt */
id?: string;
/** URL address for the webhook attempt */
address?: string;
/** Unique identifier for the webhook */
webhook_id?: number;
/** Timestamp of the attempt */
created_at?: string;
/** Max attempts allowed */
max_attempts?: number;
/** HTTP response status code for the webhook attempt */
response_status_code?: number;
/** HTTP response headers for the webhook attempt */
response_headers?: unknown;
/** HTTP response body for the webhook attempt */
response_body?: string | null;
}
interface WebhookEnvironment {
/** Environment name: 'production' | 'development' */
name: string;
/** Environment identifier */
id: string;
}
/** Core data types referenced in webhook events */
interface Asset {
id: string;
status: 'preparing' | 'ready' | 'errored';
duration?: number;
aspect_ratio?: string;
created_at: string;
playback_ids?: PlaybackID[];
mp4_support?: string;
master_access?: string;
test?: boolean;
}
interface LiveStream {
id: string;
status: 'active' | 'idle';
created_at: string;
stream_key: string;
playback_ids?: PlaybackID[];
reconnect_window?: number;
max_continuous_duration?: number;
test?: boolean;
}
interface Upload {
id: string;
url: string;
status: 'waiting' | 'asset_created' | 'errored' | 'cancelled';
new_asset_settings?: Record<string, any>;
asset_id?: string;
error?: {
type: string;
message: string;
};
cors_origin?: string;
test?: boolean;
}
interface StaticRendition {
name: string;
ext: string;
height: number;
width: number;
bitrate: number;
filesize?: number;
}
interface Track {
id: string;
type: 'video' | 'audio' | 'text';
duration?: number;
max_width?: number;
max_height?: number;
max_frame_rate?: number;
text_type?: 'subtitles' | 'captions';
language_code?: string;
name?: string;
closed_captions?: boolean;
passthrough?: string;
}
interface SimulcastTarget {
id: string;
passthrough?: string;
status?: string;
stream_key?: string;
url?: string;
}
type UnwrapWebhookEvent =
| VideoAssetCreatedWebhookEvent
| VideoAssetReadyWebhookEvent
| VideoAssetErroredWebhookEvent
| VideoAssetUpdatedWebhookEvent
| VideoAssetDeletedWebhookEvent
| VideoAssetLiveStreamCompletedWebhookEvent
| VideoAssetStaticRenditionsReadyWebhookEvent
| VideoAssetStaticRenditionsPreparingWebhookEvent
| VideoAssetStaticRenditionsDeletedWebhookEvent
| VideoAssetStaticRenditionsErroredWebhookEvent
| VideoAssetMasterReadyWebhookEvent
| VideoAssetMasterPreparingWebhookEvent
| VideoAssetMasterDeletedWebhookEvent
| VideoAssetMasterErroredWebhookEvent
| VideoAssetTrackCreatedWebhookEvent
| VideoAssetTrackReadyWebhookEvent
| VideoAssetTrackErroredWebhookEvent
| VideoAssetTrackDeletedWebhookEvent
| VideoAssetStaticRenditionCreatedWebhookEvent
| VideoAssetStaticRenditionReadyWebhookEvent
| VideoAssetStaticRenditionErroredWebhookEvent
| VideoAssetStaticRenditionDeletedWebhookEvent
| VideoAssetStaticRenditionSkippedWebhookEvent
| VideoAssetWarningWebhookEvent
| VideoAssetNonStandardInputDetectedWebhookEvent
| VideoUploadAssetCreatedWebhookEvent
| VideoUploadCancelledWebhookEvent
| VideoUploadCreatedWebhookEvent
| VideoUploadErroredWebhookEvent
| VideoLiveStreamCreatedWebhookEvent
| VideoLiveStreamConnectedWebhookEvent
| VideoLiveStreamRecordingWebhookEvent
| VideoLiveStreamActiveWebhookEvent
| VideoLiveStreamDisconnectedWebhookEvent
| VideoLiveStreamIdleWebhookEvent
| VideoLiveStreamUpdatedWebhookEvent
| VideoLiveStreamEnabledWebhookEvent
| VideoLiveStreamDisabledWebhookEvent
| VideoLiveStreamDeletedWebhookEvent
| VideoLiveStreamWarningWebhookEvent
| VideoLiveStreamSimulcastTargetCreatedWebhookEvent
| VideoLiveStreamSimulcastTargetIdleWebhookEvent
| VideoLiveStreamSimulcastTargetStartingWebhookEvent
| VideoLiveStreamSimulcastTargetBroadcastingWebhookEvent
| VideoLiveStreamSimulcastTargetErroredWebhookEvent
| VideoLiveStreamSimulcastTargetDeletedWebhookEvent
| VideoLiveStreamSimulcastTargetUpdatedWebhookEvent
| VideoDeliveryHighTrafficWebhookEvent;function handleWebhookEvent(event: UnwrapWebhookEvent) {
switch (event.type) {
// Asset events
case 'video.asset.ready':
handleAssetReady(event.data);
break;
case 'video.asset.errored':
handleAssetError(event.data);
break;
// Live stream events
case 'video.live_stream.active':
handleStreamActive(event.data);
break;
case 'video.live_stream.idle':
handleStreamIdle(event.data);
break;
// Upload events
case 'video.upload.asset_created':
handleUploadComplete(event.data);
break;
default:
console.log('Unhandled event type:', event.type);
}
}
function handleAssetReady(asset: Asset) {
console.log(`Asset ${asset.id} is ready for playback`);
// Update database, notify users, etc.
}
function handleStreamActive(stream: LiveStream) {
console.log(`Live stream ${stream.id} is now active`);
// Update stream status, notify viewers, etc.
}function isAssetEvent(event: UnwrapWebhookEvent): event is VideoAssetReadyWebhookEvent {
return event.type.startsWith('video.asset.');
}
function isLiveStreamEvent(event: UnwrapWebhookEvent): event is VideoLiveStreamActiveWebhookEvent {
return event.type.startsWith('video.live_stream.');
}
// Usage with type safety
if (isAssetEvent(event)) {
// TypeScript knows event.data is Asset
console.log('Asset duration:', event.data.duration);
}Install with Tessl CLI
npx tessl i tessl/npm-mux--mux-nodedocs