Gareth Wilson Gareth Wilson

Guide to Bunny Stream Webhooks: Features and Best Practices

Published


Bunny Stream webhooks notify your systems about video processing activity: a video finishes encoding, fails, or has captions generated. If you're building on Bunny Stream, webhooks are how you react to video status changes without polling.

This guide covers how Bunny Stream webhooks work, the Status enum, how to verify the X-BunnyStream-Signature, and the best practices for production.

What are Bunny Stream webhooks?

Bunny Stream webhooks are HTTP POSTs delivered to a URL you configure per video library, each carrying a thin payload about a video's status. Bunny signs deliveries with an X-BunnyStream-Signature header: an HMAC-SHA256 of the raw body, keyed with the library's Read-Only API key, lowercase hex.

The payload is thin (VideoLibraryId, VideoGuid, Status), so you fetch full metadata via the Stream API using the VideoGuid.

Bunny Stream webhook features

FeatureDetails
ConfigurationPer video library in the Stream dashboard
Signature headerX-BunnyStream-Signature (lowercase hex)
Signature schemeHMAC-SHA256 over the raw body
KeyThe video library's Read-Only API key
PayloadThin: VideoLibraryId, VideoGuid, Status
RetriesNot documented
SDKNone (community only)

The Status enum

The payload's Status is an integer. The full enum:

StatusMeaning
0Queued
1Processing
2Encoding
3Finished (encoding done)
4ResolutionFinished
5Failed (error)
6PresignedUploadStarted
7PresignedUploadFinished
8PresignedUploadFailed
9CaptionsGenerated
10TitleOrDescriptionGenerated

Encoding-done is 3 (Finished) and the error state is 5 (Failed). Branch on the integer Status.

Setting up Bunny Stream webhooks

Configure the webhook URL per video library in the Stream dashboard. The signing secret is that library's Read-Only API key, use it as the HMAC key.

Securing Bunny Stream webhooks

The X-BunnyStream-Signature header is a lowercase-hex HMAC-SHA256 of the raw request body, keyed with the library's Read-Only API key. Compute over the exact raw bytes and compare in constant time. (Bunny signs the raw body, not a field concatenation.)

const crypto = require("crypto");

const READ_ONLY_KEY = process.env.BUNNY_STREAM_READONLY_KEY; // library Read-Only API key

function verify(rawBody, signature) {
  const expected = crypto
    .createHmac("sha256", READ_ONLY_KEY)
    .update(rawBody)
    .digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(signature || "");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
  if (!verify(req.body, req.headers["x-bunnystream-signature"])) {
    return res.sendStatus(401);
  }

  res.sendStatus(200); // acknowledge fast
  const { VideoGuid, Status } = JSON.parse(req.body);
  processQueue.add({ VideoGuid, Status }); // fetch metadata via API, async
});

The same check in Python:

import hashlib
import hmac
import os

READ_ONLY_KEY = os.environ["BUNNY_STREAM_READONLY_KEY"].encode()


def verify(raw_body: bytes, signature: str) -> bool:
    expected = hmac.new(READ_ONLY_KEY, raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature or "")

Bunny Stream webhook limitations and pain points

The secret is the library's Read-Only API key

The Problem: There's no dedicated webhook secret, the HMAC key is the library's Read-Only API key. Using the wrong key (or another library's) fails verification.

Why It Happens: Bunny keys webhook signing on the library Read-Only API key.

Workarounds:

  • Use the correct library's Read-Only API key as the HMAC key.

How Hookdeck Can Help: Hookdeck verifies the X-BunnyStream-Signature at the edge, so the key lives in one place.

Don't confuse it with Bunny's other webhook scheme

The Problem: A different Bunny product documents an HMAC-SHA1 x-bunny-signature scheme for general (non-Stream) webhooks. Applying that to Stream (which is SHA256, X-BunnyStream-Signature) fails.

Why It Happens: Bunny has two webhook products with different schemes and headers.

Workarounds:

  • For Stream, use HMAC-SHA256 and the X-BunnyStream-Signature header.

How Hookdeck Can Help: Hookdeck verifies the Stream scheme at the edge, so your app isn't coupled to which Bunny product it's from.

Thin payload requires an API fetch

The Problem: The payload carries only VideoLibraryId, VideoGuid, and Status, so you fetch full metadata via the Stream API for anything useful.

Why It Happens: Bunny keeps the payload minimal.

Workarounds:

  • Acknowledge fast, then fetch metadata via the API using VideoGuid off a queue.

How Hookdeck Can Help: Hookdeck durably queues each event so your worker fetches metadata at a controlled rate.

Undocumented retries

The Problem: Retry behavior isn't documented, so you can't assume a failed delivery will be retried.

Why It Happens: Bunny doesn't publish the retry policy.

Workarounds:

  • Persist events on receipt and reconcile via the API; dedupe on VideoGuid + Status.

How Hookdeck Can Help: Hookdeck durably stores and retries deliveries on a schedule you control. See our guide to webhook idempotency.

Best practices

Verify with the Read-Only API key over the raw body

Compute lowercase-hex HMAC-SHA256 over the raw body with the library's Read-Only API key and compare in constant time.

Branch on the integer Status

Use 3 (Finished) for encoding-done and 5 (Failed) for errors; branch on the integer.

Acknowledge fast, fetch via the API

Return 200 immediately and fetch metadata via the Stream API using VideoGuid off a queue. See why to process webhooks asynchronously.

Dedupe and reconcile

Dedupe on VideoGuid + Status, and reconcile via the API since retries aren't documented.

Make Bunny Stream webhooks production-ready

Hookdeck verifies X-BunnyStream-Signature, deduplicates, and durably queues every video event

Conclusion

Bunny Stream webhooks verify with an X-BunnyStream-Signature HMAC-SHA256 over the raw body, keyed with the library's Read-Only API key, and carry a thin VideoGuid/Status payload (3 = Finished, 5 = Failed). Verify against the raw body, branch on the integer Status, fetch metadata via the API, and don't confuse it with Bunny's SHA1 x-bunny-signature product.

Hookdeck verifies the signature, deduplicates, and durably queues every event at the edge, so your app only ever processes verified, unique video events.

Get started with Hookdeck for free and handle Bunny Stream webhooks reliably in minutes.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.