Agent skill

Mux Webhooks Skill

Receive and verify Mux webhooks. Use when setting up Mux Video webhook handlers, debugging mux-signature verification, or handling video events like video.asset.ready, video.asset.errored, video.upload.asset_created, or video.live_stream.active.

Install this skill

npx skills add hookdeck/webhook-skills --skill mux-webhooks


When to Use This Skill

  • How do I receive Mux webhooks?
  • How do I verify the Mux webhook mux-signature header?
  • How do I know when a Mux asset is ready for playback (video.asset.ready)?
  • How do I handle Mux direct upload or live stream webhook events?
  • Why is my Mux webhook signature verification failing?

Verification (core)

Every Mux webhook carries a mux-signature header of the form t=<unix seconds>,v1=<hex>. The v1 value is HMAC-SHA256 (hex) of "<t>.<raw body>", keyed with the endpoint's signing secret. Reject timestamps older than 5 minutes (the SDK default). Always use the raw request body.

const crypto = require('crypto');

function verifyMuxSignature(rawBody, header, secret, toleranceSec = 300) {
  const parts = Object.fromEntries((header || '').split(',').map((p) => p.trim().split('=', 2)));
  const timestamp = Number(parts.t);
  if (!Number.isInteger(timestamp) || !parts.v1) return false;
  if (Math.abs(Date.now() / 1000 - timestamp) > toleranceSec) return false;

  const expected = crypto.createHmac('sha256', secret)
    .update(`${parts.t}.${rawBody}`)          // "<timestamp>.<raw body>"
    .digest('hex');
  const a = Buffer.from(expected);
  const b = Buffer.from(parts.v1);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

With the official Node SDK (@mux/ts, formerly @mux/mux-node) the same check plus JSON parsing is one call:

const event = await mux.webhooks.unwrap(rawBody, req.headers, process.env.MUX_WEBHOOK_SECRET);
// throws on a missing/invalid signature or a timestamp older than 5 minutes

For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/. Express and Next.js use the SDK; FastAPI verifies manually (the Mux Python SDK has no webhook helper).

Common Event Types

EventTriggered When
video.asset.createdAn asset has been created
video.asset.readyAsset is ready for playback via its playback_id
video.asset.erroredAsset failed (bad input URL, invalid video file, …)
video.asset.deletedAsset has been deleted and is no longer playable
video.asset.live_stream_completedA live stream ended and its recorded asset is complete
video.upload.asset_createdA direct upload finished and created an asset
video.live_stream.activeA live stream is active and can be played
video.live_stream.idleA live stream's reconnect window elapsed; status is now idle

The event type is in the payload's type field; the resource is in data, and object.id / object.type identify it.

For the full event list (assets, static renditions, tracks, uploads, live streams, simulcast targets), see references/overview.md and Mux's webhook docs.

Environment Variables

# Signing secret for THIS webhook endpoint (Dashboard > Settings > Webhooks)
MUX_WEBHOOK_SECRET=your_mux_webhook_signing_secret

Each webhook endpoint has its own signing secret. MUX_WEBHOOK_SECRET is also the variable the @mux/ts client reads by default.

Local Development

# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 mux --path /webhooks/mux

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Oct 28, 2026

View on GitHub →