Webhooks
Receive video updates and verify signed webhook events.
On this page
Register an HTTPS endpoint in the developer console and select the events you need. Save its signing secret when shown. Use Send test to validate delivery before depending on notifications.
| Event | Meaning |
|---|---|
video.ready | The editable timeline is ready. |
video.completed | The rendered output is ready to download. |
video.failed | Generation or rendering failed. Retrieve the video for details. |
video.cancelled | The video job was cancelled. |
{
"id": "evt_example",
"type": "video.completed",
"api_version": "v1",
"mode": "test",
"created_at": "2026-10-08T12:00:00Z",
"data": {
"id": "VIDEO_ID",
"status": "completed"
}
}Verify the exact request body
The Kinema-Signature header contains t=TIMESTAMP,v1=SIGNATURE. The signature is an HMAC-SHA256 of the timestamp, a period, and the exact raw body bytes, using the endpoint’s secret. Reject invalid signatures and timestamps outside a five-minute window. Do not parse and re-serialize the body before checking it.
# Verify BEFORE parsing JSON. raw_body must be the exact bytes received.
import hashlib
import hmac
import json
import time
def verify_kinema(raw_body: bytes, signature: str, secret: str):
parts = dict(item.split("=", 1) for item in signature.split(","))
timestamp = parts["t"]
if abs(time.time() - int(timestamp)) > 300:
raise ValueError("Expired webhook")
signed = timestamp.encode() + b"." + raw_body
expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, parts["v1"]):
raise ValueError("Invalid signature")
return json.loads(raw_body)
# After verification: save the event durably, deduplicate by its id,
# then respond with a 2xx. Process video work in your own background queue.
# Invalid/missing headers or parsing failures should return a 4xx.Store each event ID to ignore duplicates. Return a 2xx promptly after accepting the event durably, then perform your work in the background. Deliveries can retry or arrive out of order: retrieve the current video status when deciding what to do. Failed delivery does not undo video generation.
The console shows delivery attempts and response codes, and lets you retry a delivery. Keep polling available as a fallback.