Webhooks
Let OctaVideo call your server when a video is ready, fails, or gets its transcript.
Webhooks tell your server about events the moment they happen, so you never poll. OctaVideo sends an HTTPS POST with a JSON body to the URL you choose, signed with a secret only you and OctaVideo know.
Add an endpoint
Open API & embeds → Webhooks in the library, and select Add endpoint.
Enter your endpoint's URL. It must use https:// and answer within 10 seconds.
Pick the events to receive, then save. Copy the Signing secret (it starts with whsec_) into your server's environment, for example OCTAVIDEO_WEBHOOK_SECRET.
Each library has its own endpoints and secrets. Use Send test event to check your endpoint before going live.
Events
| Event | When it's sent | data |
|---|---|---|
video.ready | Processing finished and the video can be played. | video |
video.failed | Processing or an import failed for good. failure_reason says why. | video |
transcript.ready | A transcription finished. | video, language |
transcript.failed | A transcription failed. | video, reason |
The request
Content-Type: application/json
OctaVideo-Event: video.ready
OctaVideo-Delivery: 2b7e4f1a-9c3d-4e8a-b5f6-0a1b2c3d4e5f
OctaVideo-Signature: t=1761384900,v1=5f2b9c0e7a4d…
{
"id": "2b7e4f1a-9c3d-4e8a-b5f6-0a1b2c3d4e5f",
"type": "video.ready",
"created_at": "2026-10-25T09:35:00Z",
"library_id": "0b8e5d3a-1c2f-4e6a-8b7d-9f0a1b2c3d4e",
"data": {
"video": {
"id": "6f1c2b9e-4a7d-4f3e-9b8a-2d5c7e1f0a34",
"title": "Lesson 1: Introduction",
"encoding_status": "ready",
"progress": 100,
"duration_seconds": 754.2
}
}
}data.video has the same fields as Get a video.
Verify the signature
Always check OctaVideo-Signature before you trust a request:
- Split the header into
t(a Unix timestamp) andv1(the signature). - Compute HMAC-SHA256 of
t + "." + raw request bodywith your signing secret, as hex. - Compare it with
v1in constant time, and reject requests wheretis more than 5 minutes old.
Use the raw body bytes: parsing and re-serializing the JSON changes them and breaks the signature.
import crypto from 'node:crypto';
import express from 'express';
const app = express();
app.post('/webhooks/octavideo', express.raw({ type: 'application/json' }), (req, res) => {
const header = req.get('OctaVideo-Signature') ?? '';
const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
const expected = crypto
.createHmac('sha256', process.env.OCTAVIDEO_WEBHOOK_SECRET)
.update(`${parts.t}.${req.body}`)
.digest('hex');
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
const valid =
parts.v1?.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
if (!fresh || !valid) return res.sendStatus(400);
const event = JSON.parse(req.body);
// Queue the work, then answer quickly.
res.sendStatus(204);
});The SDKs include a helper that does these checks for you.
Retries and duplicates
- Answer with any
2xxstatus within 10 seconds. Do slow work after answering, for example in a queue. - Any other answer, or no answer, is retried with a growing delay for up to 24 hours.
- The same event can arrive more than once, and events can arrive out of order. Use
idto skip events you've already handled, and get the video when you need its latest state. - Webhooks in the console lists each delivery with its status and response, and can resend one.
Keep the secret secret
Anyone with the signing secret can forge events. If it leaks, select Roll secret on the endpoint and update your server.