Overview
Webhooks allow you to receive real-time notifications when your video processing completes, instead of polling the API repeatedly.
Recommended : Always use webhooks for production applications. Polling is inefficient and can hit rate limits.
How Webhooks Work
Provide Webhook URL
When creating a video, include your webhook URL {
"type" : "class" ,
"script" : "Your script..." ,
"webhook" : "https://yoursite.com/api/webhook"
}
Video Processing
SlideVid processes your video (typically 2-5 minutes)
Receive Notification
When complete, SlideVid sends a POST request to your webhook URL with video details
Return 200 OK
Your endpoint must return a 200 status code to confirm receipt
Webhook Payload
When your video is ready, you’ll receive this payload:
{
"event" : "project.completed" ,
"projectId" : "proj_abc123" ,
"status" : "completed" ,
"video" : {
"id" : "video_xyz789" ,
"url" : "https://cdn.tryslidevid.ai/videos/xyz789.mp4" ,
"duration" : 45 ,
"thumbnail" : "https://cdn.tryslidevid.ai/thumbnails/xyz789.jpg" ,
"aspectRatio" : "9:16" ,
"resolution" : "1080x1920"
},
"createdAt" : "2024-01-15T10:30:00Z" ,
"completedAt" : "2024-01-15T10:33:00Z"
}
Payload Fields
Event type: project.completed or project.failed
Status: completed or failed
Video details (only present if status is completed) Direct download URL for the video (expires in 7 days)
Video duration in seconds
URL to video thumbnail image
Error message (only present if status is failed)
Implementing a Webhook Endpoint
Node.js (Express)
const express = require ( 'express' );
const app = express ();
app . use ( express . json ());
app . post ( '/api/webhook' , async ( req , res ) => {
const { event , projectId , status , video } = req . body ;
console . log ( `Received webhook: ${ event } for ${ projectId } ` );
if ( event === 'project.completed' && status === 'completed' ) {
// Video is ready!
console . log ( 'Video URL:' , video . url );
console . log ( 'Duration:' , video . duration , 'seconds' );
try {
// Save to database
await saveVideoToDatabase ({
projectId ,
videoUrl: video . url ,
thumbnail: video . thumbnail ,
duration: video . duration
});
// Notify user
await notifyUser ( projectId , video . url );
// Download and store (optional)
await downloadAndStore ( video . url , projectId );
} catch ( error ) {
console . error ( 'Error processing webhook:' , error );
// Still return 200 to prevent retries for application errors
}
} else if ( event === 'project.failed' ) {
console . error ( 'Video failed:' , req . body . message );
await notifyUserOfFailure ( projectId );
}
// IMPORTANT: Always return 200
res . status ( 200 ). send ( 'OK' );
});
app . listen ( 3000 );
Python (FastAPI)
from fastapi import FastAPI, Request
import httpx
app = FastAPI()
@app.post ( "/api/webhook" )
async def webhook_handler ( request : Request):
payload = await request.json()
event = payload.get( "event" )
project_id = payload.get( "projectId" )
status = payload.get( "status" )
print ( f "Received webhook: { event } for { project_id } " )
if event == "project.completed" and status == "completed" :
video = payload.get( "video" )
# Save to database
await save_video_to_db({
"project_id" : project_id,
"video_url" : video[ "url" ],
"thumbnail" : video[ "thumbnail" ],
"duration" : video[ "duration" ]
})
# Notify user
await notify_user(project_id, video[ "url" ])
elif event == "project.failed" :
await notify_failure(project_id, payload.get( "message" ))
# Always return 200
return { "status" : "ok" }
Next.js API Route
import { NextRequest , NextResponse } from 'next/server' ;
export async function POST ( req : NextRequest ) {
try {
const payload = await req . json ();
const { event , projectId , status , video } = payload ;
if ( event === 'project.completed' && status === 'completed' ) {
// Process completed video
await processCompletedVideo ({
projectId ,
videoUrl: video . url ,
thumbnail: video . thumbnail ,
duration: video . duration
});
}
return NextResponse . json ({ received: true }, { status: 200 });
} catch ( error ) {
console . error ( 'Webhook error:' , error );
// Still return 200 to prevent retries
return NextResponse . json ({ error: 'Internal error' }, { status: 200 });
}
}
Webhook Security
Verify Webhook Source
Always verify webhooks are coming from SlideVid. Check the request origin and consider implementing HMAC signature verification.
// Verify request came from SlideVid's IP ranges
const allowedIPs = [ '52.1.2.3' , '54.2.3.4' ]; // Example IPs
app . post ( '/api/webhook' , ( req , res ) => {
const clientIP = req . ip || req . headers [ 'x-forwarded-for' ];
if ( ! allowedIPs . includes ( clientIP )) {
return res . status ( 403 ). send ( 'Forbidden' );
}
// Process webhook...
});
Use HTTPS
Always use HTTPS for your webhook endpoints to ensure data is encrypted in transit.
✅ https://yoursite.com/webhook
❌ http://yoursite.com/webhook
Secret Tokens
Include a secret token in your webhook URL for additional security:
// When creating video
{
"webhook" : "https://yoursite.com/webhook?token=your_secret_token"
}
// In your webhook handler
app . post ( '/webhook' , ( req , res ) => {
const token = req . query . token ;
if ( token !== process . env . WEBHOOK_SECRET ) {
return res . status ( 401 ). send ( 'Unauthorized' );
}
// Process webhook...
});
Retry Logic
If your endpoint doesn’t return a 200 status code, SlideVid will retry:
1st retry : After 1 minute
2nd retry : After 5 minutes
3rd retry : After 15 minutes
After 3 failed attempts, no more retries will be made.
Best Practices for Retries
app . post ( '/webhook' , async ( req , res ) => {
// Return 200 FIRST
res . status ( 200 ). send ( 'OK' );
// Then process asynchronously
setImmediate ( async () => {
try {
await processWebhook ( req . body );
} catch ( error ) {
// Handle error without affecting the HTTP response
console . error ( 'Async processing error:' , error );
await logToErrorTracking ( error );
}
});
});
Testing Webhooks
Local Development with ngrok
# Install ngrok
npm install -g ngrok
# Start your local server
node server.js # Running on localhost:3000
# Create tunnel
ngrok http 3000
# Use the ngrok URL as your webhook
# https://abc123.ngrok.io/api/webhook
Webhook Testing Sites
Test Webhook Endpoint
# Test your webhook manually
curl -X POST https://yoursite.com/api/webhook \
-H "Content-Type: application/json" \
-d '{
"event": "project.completed",
"projectId": "test_123",
"status": "completed",
"video": {
"url": "https://example.com/test.mp4",
"duration": 30,
"thumbnail": "https://example.com/thumb.jpg"
}
}'
Common Issues
Possible causes:
Webhook URL not publicly accessible
Firewall blocking SlideVid’s IP addresses
Endpoint returning non-200 status code
Solution: Test with webhook.site first, then gradually move to your endpoint
Possible causes:
Your endpoint took too long to respond
Endpoint returned non-200, triggering retry
Solution: Implement idempotency using projectIdconst processedProjects = new Set ();
app . post ( '/webhook' , ( req , res ) => {
const { projectId } = req . body ;
if ( processedProjects . has ( projectId )) {
return res . status ( 200 ). send ( 'Already processed' );
}
processedProjects . add ( projectId );
// Process webhook...
});
Problem: Your endpoint is slow, causing timeoutsSolution: Return 200 immediately, process asynchronouslyapp . post ( '/webhook' , async ( req , res ) => {
// Respond immediately
res . status ( 200 ). send ( 'OK' );
// Process in background
processWebhookAsync ( req . body );
});
Webhook Events
Currently supported events:
Event Description
project.completedVideo successfully generated and ready to download project.failedVideo generation failed due to an error
Next Steps