Callback
When the video state changes, a JSON callback will be sent back to your URL as a POST request. Below is an example object that will be sent.IsLiveStreamWebhook, EventId, Timestamp, and Reason were added to an existing payload. If your receiver ignores fields it doesn’t know, it keeps working without changes. Switch on the numeric Status value, not on the status name.Status list
Below are the possible status codes sent with the webhook:- 0 - Queued: The video has been queued for encoding.
- 1 - Processing: The video has begun processing the preview and format details.
- 2 - Encoding: The video is encoding.
- 3 - Finished: The video encoding has finished and the video is fully available.
- 4 - Resolution finished: The encoder has finished processing one of the resolutions. The first request also signals that the video is now playable.
- 5 - Failed: The video encoding failed. The video has finished processing.
- 6 - PresignedUploadStarted : A pre-signed upload has been initiated.
- 7 - PresignedUploadFinished : A pre-signed upload has been completed.
- 8 - PresignedUploadFailed : A pre-signed upload has failed.
- 9 - CaptionsGenerated : Automatic captions were generated.
- 10 - TitleOrDescriptionGenerated : Automatic generation of title or description has been completed.
- 11 - CaptionGenerationFailed : Automatic caption generation has failed.
- 12 - ChaptersGenerated : Automatic chapters were generated.
- 13 - MomentsGenerated : Automatic moments were generated.
- 32 - VideoDeleted : The video was deleted, including when it was deleted together with its collection. Sent once per deleted video.
- 33 - SmartGenerationFailed : A requested smart generation feature failed.
Reasonnames the feature:Title,Description,Chapters, orMoments. One webhook is sent for each failed feature. Features that completed before the failure still send their own generated webhook.
Delivery and retries
Each webhook is first sent right after the event happens. Your endpoint confirms delivery by responding with any2xx status code.
If a delivery fails with a network error, a timeout, or a 408, 429, or 5xx response, it is retried after 30 seconds, 2 minutes, 10 minutes, 30 minutes, 1 hour, and 2 hours. After the last attempt, about four hours after the event, the webhook is dropped. Other 4xx responses are not retried.
- Same body on every attempt: A retry sends the original body, so
EventIdandTimestampdon’t change. UseEventIdto ignore a webhook you already processed. - Order by
Timestamp: A retried webhook can arrive after a newer event for the same video. CompareTimestampvalues instead of relying on arrival order. - Current settings apply: Every attempt goes to the library’s current webhook URL and is signed with its current Read-Only API key. If you remove the webhook URL, pending retries are dropped.
- Respond quickly: Return a
2xxresponse as soon as you’ve received the webhook, and do slow processing afterwards, so the request doesn’t time out and get retried.
Signature validation
Verify that an incoming webhook request was sent by Bunny Stream and has not been tampered with.Overview
Signed webhook POSTs use signature versionv1. Each signed request includes the following headers:
The signature is an HMAC-SHA256 of the exact raw request body, using the webhook signing secret as the key, encoded as a lowercase hex string.
How the signature is built
- body: The exact raw HTTP request body bytes as received.
- signatureSecret: Your library’s Read-Only API key, encoded as UTF-8.
- Output: 64-character lowercase hexadecimal string.
- The URL, HTTP method, and headers are not part of the
v1signature. The body, including itsEventIdandTimestampfields, is.
Validation steps
1
Read the raw request body
Capture the raw body from the HTTP request stream before any parsing. Do not parse to JSON and re-serialize.
2
Check the version header
Ensure
X-BunnyStream-Signature-Version is v1.3
Check the algorithm header
Ensure
X-BunnyStream-Signature-Algorithm is hmac-sha256.4
Get your signing secret
Use your library’s Read-Only API key as the signing secret.
5
Compute the expected signature
expectedSignature = lowercase_hex(HMAC-SHA256(rawBody, signatureSecret))6
Compare signatures
Read
X-BunnyStream-Signature and compare it to your computed value using a constant-time comparison to avoid timing attacks. If they match, the request is authentic and the body was not modified in transit.Code examples
Important notes
- Constant-time comparison: Use
timingSafeEqual,hmac.compare_digest, orCryptographicOperations.FixedTimeEqualsso comparison time does not leak information about the secret. - Signing secret: Use your library’s Read-Only API key as the signing secret. Keep it server-side only; never expose it to the client.
- Version & algorithm headers: Always validate both
X-BunnyStream-Signature-VersionandX-BunnyStream-Signature-Algorithmbefore computing the signature.