Shukr Docs
Shukr Public Media Service

Webhooks

Inbound provider callbacks that advance a video's transcode status — Bunny, on-prem, and Cloudflare.

Streaming providers call these endpoints when a video finishes uploading or transcoding. They flip the asset's stream_status (pending_upload → processing → ready / failed) and populate its playback manifest.

You register these URLs with each provider — you don't call them yourself. They are not behind the user auth guard; instead each is protected by the provider's signature verification. Every handler returns 200 with a { processed } result, or the standard error envelope on failure.

Result shape
{
  "success": true,
  "message": "Webhook processed",
  "code": 200,
  "data": { "processed": true },
  "request_id": "req_01J9Z0X8QE",
  "timestamp": "2026-07-29T04:21:00.000Z"
}

processed: true means an asset was matched and updated. processed: false means the payload was valid but didn't match a known asset (or wasn't actionable) — the request still returns 200 so the provider doesn't retry needlessly.


Bunny Stream

POST /api/v1/webhooks/stream/bunny200 OK

What it is — Bunny's callback reporting a video's upload/encoding status; it advances the matching asset's stream_status and stores playback URLs once ready.

When to use it — You don't call it — you register the URL in Bunny. Bunny calls it as the video progresses.

How to use it — Register the endpoint in your Bunny library. Bunny signs each request with a v1 HMAC-SHA256 signature derived from your library Read-Only API key.

Scope — Public (no auth token); authenticated by the signature.

BUNNY_STREAM_READ_ONLY_API_KEY must be set. Requests without a valid v1 HMAC signature are rejected with 401 UNAUTHORIZED — this signature check is mandatory, not optional.

Bunny's numeric Status maps to stream_status as follows:

Bunny StatusMeaningstream_status
3, 4Finished / playableready
7TUS/upload finished, encoding continuesprocessing
5, 8Errorfailed
curl -X POST https://media.example.com/api/v1/webhooks/stream/bunny \
  -H "Content-Type: application/json" \
  -d '{ "VideoLibraryId": 12345, "VideoGuid": "e7c2...", "Status": 3 }'

On-prem encoder

POST /api/v1/webhooks/stream/onprem200 OK

What it is — Your own encoder's callback reporting transcode progress/completion, carrying the asset_id, a status, and the generated playback URLs.

When to use it — Your encoder calls it after POST /media/{id}/complete triggers encoding, and again when the video is ready or failed.

How to use it — Sign the raw body as sha256(rawBody + secret) in X-Onprem-Signature (or X-Webhook-Signature). Playback URLs from the body are stored only when the status is ready.

Scope — Public (no auth token). Set ONPREM_WEBHOOK_SECRET to enforce the signature (if unset, verification is skipped).

curl -X POST https://media.example.com/api/v1/webhooks/stream/onprem \
  -H "Content-Type: application/json" \
  -H "X-Signature: <hmac-sha256>" \
  -d '{
    "asset_id": "770e8400-e29b-41d4-a716-4466554400ff",
    "status": "ready",
    "playback": {
      "hls": "https://media.example.com/api/v1/playback/770e8400.../hls.m3u8",
      "dash": "https://media.example.com/api/v1/playback/770e8400.../dash.mpd",
      "thumbnail": "https://media.example.com/api/v1/playback/770e8400.../thumb.jpg"
    }
  }'

Cloudflare Stream

POST /api/v1/webhooks/stream/cloudflare200 OK

What it is — Cloudflare Stream's callback when a video finishes processing or errors; it advances the matching asset's stream_status and stores playback once ready.

When to use it — You don't call it — register the URL once per account via PUT /accounts/{account_id}/stream/webhook. Cloudflare calls it on completion.

How to use it — Cloudflare signs with HMAC-SHA256 over {time}.{rawBody} in the Webhook-Signature: time=…,sig1=… header.

Scope — Public (no auth token). Set CLOUDFLARE_STREAM_WEBHOOK_SECRET to enforce the signature (if unset, verification is skipped).

curl -X POST https://media.example.com/api/v1/webhooks/stream/cloudflare \
  -H "Content-Type: application/json" \
  -H "Webhook-Signature: time=1785000000,sig1=<hmac-sha256>" \
  -d '{ "uid": "abc123", "status": { "state": "ready" } }'

If a provider is not configured (its secret/key is unset) its webhook returns 401 "not configured". Invalid JSON returns 401 "Invalid webhook payload". An unrecognized but well-formed payload returns 200 with { "processed": false }.

On this page