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-signatureheader? - 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
| Event | Triggered When |
|---|---|
video.asset.created | An asset has been created |
video.asset.ready | Asset is ready for playback via its playback_id |
video.asset.errored | Asset failed (bad input URL, invalid video file, …) |
video.asset.deleted | Asset has been deleted and is no longer playable |
video.asset.live_ | A live stream ended and its recorded asset is complete |
video.upload.asset_ | A direct upload finished and created an asset |
video.live_stream.active | A live stream is active and can be played |
video.live_stream.idle | A 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
- references/overview.md - Mux webhook concepts, full event list, payload shape
- references/setup.md - Dashboard and API configuration, signing secret
- references/verification.md - Signature verification details and gotchas