Agent skill

Vimeo Webhooks Skill

Receive and verify Vimeo app webhooks. Use when setting up Vimeo webhook handlers, debugging X-Webhook-Signature verification, or handling video events like video-transcode-complete, video-deleted, video-upload-failed, and live-event-started.

Install this skill

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


When to Use This Skill

  • How do I receive Vimeo webhooks?
  • How do I verify the Vimeo X-Webhook-Signature header?
  • Why does my Vimeo webhook signature verification fail, or why are deliveries unsigned?
  • How do I handle video-transcode-complete, video-deleted, or live-event-started events?
  • How do I set a secret on a Vimeo webhook through the API?

Verification (core)

Vimeo signs a delivery only if the webhook has a secret set. The X-Webhook-Signature header is the lowercase hex HMAC-SHA256 of the raw request body, keyed with that secret. There is no timestamp in the signed content and no prefix on the header value.

Node (check the format first: Buffer.from(x, 'hex') silently truncates invalid hex):

const crypto = require('crypto');

function verifyVimeo(rawBody, signature, secret) {
  if (!signature || !/^[0-9a-f]{64}$/i.test(signature)) return false;
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  // Equal-length lowercase hex strings, compared in constant time
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature.toLowerCase()));
}

Python (encode both sides to bytes so a non-ASCII header returns 401, not 500):

import hashlib, hmac

def verify_vimeo(raw_body: bytes, signature: str | None, secret: str) -> bool:
    if not signature:
        return False
    expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected.encode(), signature.lower().encode())

For complete handlers with route wiring, event dispatch, and tests, see:

Setup: set a secret on the webhook

Vimeo has no dashboard for app webhooks; you create them through the API (POST /apps/{app_id}/webhooks) and set secret in the same request, or later with PATCH /apps/{app_id}/webhooks/{webhook_id}. Vimeo recommends a random string of at least 64 bytes. Set a secret on the webhook through the Vimeo API. Without one, deliveries are unsigned and fail verification (whether in your handler or in a Hookdeck source with verification on). See references/setup.md for the requests.

For Hookdeck, create a source of type Vimeo and paste the same secret as its webhook signing secret.

Common Event Types

The event name is in the body's webhook_type field (not a header). Payloads look like { "webhook_type": "video-transcode-complete", "data": { ... }, "timestamp": 1644961452 }.

Event (webhook_type)Fires when
video-createdA user creates a video
video-deletedA user deletes a video
video-transcode-playableA video is ready for playback
video-transcode-completeA video finished transcoding
video-upload-failedA video upload fails
transcript-status-completeA video's transcript completed or failed
live-event-startedA live event goes live
live-event-endedA live event ends

Other documented types: live-event-updated, live-event-archive-available, live-event-clip-created, video-transcode-fully-playable, automatic-thumbnail-available, content-scan-completed, registration-created, registration-updated, registration-deleted.

For the full event reference, see Vimeo app webhooks and references/overview.md.

Important Headers

HeaderDescription
X-Webhook-SignatureHex HMAC-SHA256 of the raw body; only sent when the webhook has a secret
Content-TypeContent type of the payload body

Vimeo documents no delivery ID header. See references/overview.md for deduplicating.

Environment Variables

VIMEO_WEBHOOK_SECRET=your_webhook_secret   # The `secret` you set on the webhook via the Vimeo API

Local Development

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

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 9, 2026

View on GitHub →