Skip to main content

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

1

Provide Webhook URL

When creating a video, include your webhook URL
{
  "type": "class",
  "script": "Your script...",
  "webhook": "https://yoursite.com/api/webhook"
}
2

Video Processing

SlideVid processes your video (typically 2-5 minutes)
3

Receive Notification

When complete, SlideVid sends a POST request to your webhook URL with video details
4

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
string
Event type: project.completed or project.failed
projectId
string
The project/video ID
status
string
Status: completed or failed
video
object
Video details (only present if status is completed)
message
string
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

webhook.site

Get a temporary webhook URL for testinghttps://webhook.site

requestbin.com

Inspect webhook payloadshttps://requestbin.com

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 projectId
const 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 asynchronously
app.post('/webhook', async (req, res) => {
  // Respond immediately
  res.status(200).send('OK');
  
  // Process in background
  processWebhookAsync(req.body);
});

Webhook Events

Currently supported events:
EventDescription
project.completedVideo successfully generated and ready to download
project.failedVideo generation failed due to an error

Next Steps