Webhooks
Receive real-time notifications when video generations complete.
Pipevideo delivers webhooks over HTTPS when you pass a webhook_url on POST /v1/responses. Each request can target a different URL — there are no persistent endpoint URLs to configure.
Setup
- Sign in to pipevideo.co.
- Open Webhooks in the sidebar.
- Choose which video generation lifecycle events to deliver (
completedis enabled by default). - Copy your signing secret for signature verification.
- Include
webhook_urlwhen creating a generation:
curl -X POST https://api.pipevideo.co/v1/responses \
-H "Authorization: Bearer $PIPEVIDEO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "moonshotai/kimi-k2.5",
"input": "A product launch teaser",
"webhook_url": "https://your-app.com/api/webhooks/pipevideo"
}'If you omit webhook_url, poll GET /v1/responses/{id} instead. Event toggles in the dashboard only apply when a request includes webhook_url.
Event types
| Event type | Description |
|---|---|
video.generation.queued | Fired when a request is accepted and waiting to run (API status: queued). |
video.generation.started | Fired when processing begins with the provider (API status: in_progress). |
video.generation.completed | Fired when a generation finishes, whether it succeeded or failed. |
Use data.object.status on video.generation.completed to distinguish success vs failure.
Payload format
Webhook bodies follow a Stripe-style event envelope. All field names are snake_case, matching the REST API.
| Field | Type | Description |
|---|---|---|
id | string | Unique event ID (e.g. evt_j57abc123). |
object | "event" | Always "event". |
created | number | Unix timestamp (seconds) when the event was created. |
type | string | Event type (e.g. "video.generation.completed"). |
data.object | object | The generation resource that changed. |
Generation object (data.object)
| Field | Type | Description |
|---|---|---|
id | string | Pipevideo generation ID. Correlates with POST /v1/responses response id. |
object | "video.generation" | Always "video.generation". |
status | string | queued, in_progress, completed, or failed. |
engine | string | Rendering engine (e.g. hyperframes). Present on queued/started events. |
model | string | Orchestration model slug. Present on queued/started events. |
provider | string | Inference provider slug. Present on queued/started events. |
output_url | string | null | Video download URL when completed. null when failed. |
error_message | string | null | Error details when failed. null when completed. |
Completed example
{
"id": "evt_j57abc123",
"object": "event",
"created": 1700000000,
"type": "video.generation.completed",
"data": {
"object": {
"id": "gen_abc123xyz",
"object": "video.generation",
"status": "completed",
"output_url": "https://storage.example.com/videos/abc123.mp4",
"error_message": null
}
}
}Failed example
{
"id": "evt_j57abc456",
"object": "event",
"created": 1700000001,
"type": "video.generation.completed",
"data": {
"object": {
"id": "gen_abc123xyz",
"object": "video.generation",
"status": "failed",
"output_url": null,
"error_message": "Provider callback timed out"
}
}
}Request headers
Each delivery includes these headers for signature verification:
| Header | Description |
|---|---|
pipevideo-webhook-id | Unique message ID — use for idempotency |
pipevideo-webhook-timestamp | Unix timestamp (seconds) when the message was sent |
pipevideo-webhook-signature | HMAC signature over {id}.{timestamp}.{body} |
Verifying signatures
Pipevideo signs each webhook with HMAC-SHA256 using your organization's signing secret (shown in the dashboard). Read the raw request body before parsing JSON, then verify the signature:
import { createHmac, timingSafeEqual } from "node:crypto";
function verifyPipevideoWebhook(secret, payload, headers) {
const id = headers["pipevideo-webhook-id"];
const timestamp = headers["pipevideo-webhook-timestamp"];
const signatureHeader = headers["pipevideo-webhook-signature"];
const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64");
const signedContent = `${id}.${timestamp}.${payload}`;
const expected = createHmac("sha256", key)
.update(signedContent, "utf8")
.digest("base64");
const signatures = signatureHeader
.split(" ")
.filter((part) => part.startsWith("v1,"))
.map((part) => part.slice(3));
return signatures.some((signature) => {
const a = Buffer.from(signature);
const b = Buffer.from(expected);
return a.length === b.length && timingSafeEqual(a, b);
});
}
const payload = await req.text();
const valid = verifyPipevideoWebhook(
process.env.PIPEVIDEO_WEBHOOK_SECRET,
payload,
{
"pipevideo-webhook-id": req.headers.get("pipevideo-webhook-id"),
"pipevideo-webhook-timestamp": req.headers.get("pipevideo-webhook-timestamp"),
"pipevideo-webhook-signature": req.headers.get("pipevideo-webhook-signature"),
}
);
if (!valid) {
return new Response("Invalid signature", { status: 401 });
}
const body = JSON.parse(payload);
// body.type — event type
// body.data.object.id — generation IDReject requests with invalid signatures (return 401). Optionally reject timestamps more than five minutes old to limit replay attacks.
Retry behavior
Failed deliveries are retried automatically with exponential backoff (up to several attempts over ~24 hours). Return 2xx within 15 seconds for successful delivery.
Local development
webhook_url must be a public HTTPS URL. Pipevideo rejects localhost, private IPs, and link-local addresses to prevent SSRF.
To test locally:
- Run a tunnel (e.g. ngrok) or use a request inspector like webhook.site.
- Pass the public URL as
webhook_urlonPOST /v1/responses. - Enable the events you want under Webhooks in the dashboard.
In local dev, delivery is triggered directly from the API workflow — no extra configuration is required beyond running Inngest alongside the API.
Best practices
- Respond quickly — return
200immediately and process asynchronously. - Be idempotent — use
data.object.idto deduplicate; the same event may be delivered more than once. - Return non-2xx on failure — so Pipevideo retries if you cannot process the payload.
Next steps
- Video generation guide — create and poll generations.
- Quickstart — submit your first video.