Webhooks
Converting your video to the desired resolutions is fast but it is not immediate. As such we need to notify you on completion so you can safely start using your video. Hyperserve uses webhooks to give your application real time updates for status changes on your video.
Create your webhook in the dashboard
- Navigate to the Webhooks page in the dashboard.
- Click on the `Create` button.
- Fill in the `Name` and `Url` fields. The `Url` should be the endpoint where you want to receive the webhook events.
- Click `Create` to finish.
The system will automatically generate a secret key for your webhook. Copy this secret and store it in your database, it is used to verify that the incoming requests are from Hyperserve.
Setup a webhook receiver
Next, you need to set up a receiver in your application or server to handle the incoming webhook events. Here's a basic example in Node.js using Express:
- SDK
- Raw HTTP
import express from 'express';
import { verifyWebhookSignature } from '@hyperserve/hyperserve-js';
const app = express();
// express.raw preserves the body as a Buffer so the signature can be verified.
// Do not use express.json() globally for this route — re-serializing JSON can
// change whitespace and invalidate the signature.
app.post('/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
const isValid = await verifyWebhookSignature({
signature: req.headers['x-hyperserve-signature'] ?? '',
secret: process.env.HYPERSERVE_WEBHOOK_SECRET,
body: req.body.toString(),
});
if (!isValid) {
return res.status(401).end();
}
const event = JSON.parse(req.body.toString());
console.log(event);
// Process the webhook event.
// For example, if processing is complete you could save the
// video id for retrieval at runtime.
res.sendStatus(200);
});
app.listen(3000, () => console.log('Webhook receiver running on port 3000'));
Using Next.js App Router?
import { verifyWebhookSignature } from '@hyperserve/hyperserve-js';
export async function POST(request) {
const body = await request.text();
const isValid = await verifyWebhookSignature({
signature: request.headers.get('x-hyperserve-signature') ?? '',
secret: process.env.HYPERSERVE_WEBHOOK_SECRET,
body,
});
if (!isValid) return new Response(null, { status: 401 });
const event = JSON.parse(body);
// Process the webhook event.
return new Response(null, { status: 200 });
}
const express = require('express');
const crypto = require('crypto');
const app = express();
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-hyperserve-signature'] ?? '';
const secret = process.env.HYPERSERVE_WEBHOOK_SECRET;
const rawBody = req.body.toString();
const [timestamp, receivedSignature] = signature.split('.');
// Reject requests with a timestamp older than 5 minutes
const MAX_AGE_MS = 5 * 60 * 1000;
if (Math.abs(Date.now() - parseInt(timestamp, 10)) > MAX_AGE_MS) {
return res.status(401).end();
}
// HMAC covers timestamp + "." + raw body for replay and integrity protection
const hmac = crypto.createHmac('sha256', secret);
const expectedBuf = hmac.update(timestamp + '.' + rawBody).digest();
const receivedBuf = Buffer.from(receivedSignature, 'hex');
// Use timingSafeEqual to prevent timing attacks. Lengths must match first —
// timingSafeEqual throws if they differ, which would leak length information.
const isValid =
receivedBuf.length === expectedBuf.length &&
crypto.timingSafeEqual(receivedBuf, expectedBuf);
if (!isValid) {
return res.status(401).end();
}
const event = JSON.parse(rawBody);
console.log(event);
// Process the webhook event.
// For example, if processing is complete you could save the
// video id for retrieval at runtime.
res.sendStatus(200);
});
app.listen(3000, () => console.log('Webhook receiver running on port 3000'));
This server listens for POST requests at the /webhook endpoint. When it receives a request, it verifies the x-hyperserve-signature header to ensure the request is from Hyperserve. If the signature is invalid or the timestamp is more than 5 minutes old, it returns a 401 response.
Receiving webhooks
The processing complete event will contain all the necessary data about your video. Urls for public videos will be available now, private videos will just have ids as signed urls need to be retrieved at runtime. See the public and private videos guide for more information.
Refer to the video-processing-success webhook reference for more information on what data is sent with the webhook.
That's it! Your webhook is all setup, we will send you any events related to your videos here.