Webhooks API
Webhooks send an HTTP POST to your URL when something happens in your account, so you do not have to poll. Manage them in the portal (Webhooks) or with the endpoints below.
Events
| Event | When | Fields in the payload |
|---|---|---|
video.uploaded | A video finished uploading | video_id, video_title, library_id |
video.transcode.complete | A video is ready to play | video_id, video_title, library_id, duration, master_url |
video.transcode.failed | Transcoding failed | video_id, video_title, library_id, error |
pullzone.created / pullzone.deleted | A pull zone was created or deleted (portal or API) | pull_zone_id, label, cdn_hostname |
storage.bucket.created / storage.bucket.deleted | A bucket was created or deleted (portal or API) | bucket_id, bucket_name |
live.stream.started / live.stream.ended | A live stream went live or stopped | stream_id, stream_slug, stream_name |
payment.succeeded | An invoice was paid | invoice_id, invoice_number, amount, currency, gateway |
Every payload also has event, delivery (a unique id) and created_at:
{
"event": "live.stream.started",
"delivery": "5f0c2a2e-8a3b-4b7e-9f55-2f7a1c3d9e10",
"created_at": "2026-09-26T18:13:57+00:00",
"stream_id": 16, "stream_slug": "friday-show-x1y2z3", "stream_name": "Friday show"
}
Verify that a delivery is from CDNShark
Each request carries these headers:
X-CDNShark-Event: the event name.X-CDNShark-Delivery: the delivery id.X-CDNShark-Signature: sha256=<hex>: the HMAC-SHA256 of the raw request body, keyed with your webhook's secret.
// PHP
$expected = 'sha256=' . hash_hmac('sha256', file_get_contents('php://input'), $secret);
if (! hash_equals($expected, $_SERVER['HTTP_X_CDNSHARK_SIGNATURE'] ?? '')) { http_response_code(401); exit; }
// Node.js (Express: use express.raw() so you get the raw body)
const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(req.body).digest('hex');
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(req.get('X-CDNShark-Signature') || ''));
Delivery and retries
Answer with any 2xx within 10 seconds. If your server cannot be reached or times out, we retry up to 3 times: after 5 minutes, 30 minutes and 2 hours. A delivery your server answers with a non-2xx status is recorded as failed and not retried. Use delivery to ignore duplicates.
Endpoints
| Method | Path | Permission | What it does |
|---|---|---|---|
| GET | /api/webhooks | webhooks:read | Your webhooks (an array). |
| POST | /api/webhooks | webhooks:write | Create. Body: label (up to 120 characters), url, events (an array of event names). Returns 201 with secret, shown once. Up to 20 per account. |
| PUT | /api/webhooks/{id} | webhooks:write | Change any of label, url, events, is_active. |
| DELETE | /api/webhooks/{id} | webhooks:write | Delete. |
| GET | /api/webhooks/{id}/logs | webhooks:read | The last 100 deliveries: event, attempts, HTTP status, errors. |
| GET | /api/webhooks/events | webhooks:read | Every event name with a description. |
curl -X POST https://cdnshark.com/api/webhooks \
-H "Authorization: Bearer YOUR_TOKEN" -H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"label": "Live notifications", "url": "https://example.com/hooks/cdnshark",
"events": ["live.stream.started", "live.stream.ended"]}'