Webhook Callbacks
Instead of polling the status endpoint, you can provide a callback URL when submitting tasks to receive automatic notifications when generation completes.
How It Works
- Submit with callback URL: Include a
callback_url in your task submission
- Receive notification: When the task completes (success or failure), Vidgo API sends a POST request to your URL
- Process result: Your server receives the complete task data including generated files
Callback Request
When a task completes, Vidgo API will send a POST request to your callback URL with the same structure as the status endpoint response:
{
"code": 200,
"data": {
"task_id": "task-unified-1757165031-uyujaw3d",
"status": "finished",
"files": [
{
"file_url": "https://storage.vidgo.ai/generated/image-abc123.jpg",
"file_type": "image"
}
],
"created_time": "2025-11-12T10:30:00",
"progress": 100,
"error_message": null
}
}
Failed Task Callback
{
"code": 200,
"data": {
"task_id": "task-unified-1757165031-uyujaw3d",
"status": "failed",
"files": [],
"created_time": "2025-11-12T10:30:00",
"progress": 0,
"error_message": "The prompt violates our content policy"
}
}
Requirements
Your callback endpoint must meet the following requirements:
- HTTPS only: Must use HTTPS protocol (HTTP not supported)
- Maximum URL length: 2048 characters
- Response timeout: Must respond within 10 seconds
- Success response: Should return HTTP 200-299 status code
- No internal IPs: Cannot use private/internal IP addresses (e.g., 192.168.x.x, 10.x.x.x)
- Public accessibility: Must be publicly accessible from the internet
Retry Policy
If your callback endpoint fails to respond or returns an error:
- Retry attempts: 3 automatic retries
- Retry delays: After 1 second, 2 seconds, and 4 seconds
- Final failure: After 3 failed attempts, no further retries are made
If all retry attempts fail, you can still retrieve the results by polling the status endpoint.
Example Implementation
Python (Flask)
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/webhook/generation-complete', methods=['POST'])
def handle_generation_complete():
# Parse the callback payload
data = request.json
task_data = data.get('data', {})
task_id = task_data.get('task_id')
status = task_data.get('status')
if status == 'finished':
# Task completed successfully
files = task_data.get('files', [])
for file in files:
print(f"Generated file: {file['file_url']}")
# Download and process the file
# save_file(file['file_url'])
elif status == 'failed':
# Task failed
error = task_data.get('error_message')
print(f"Task {task_id} failed: {error}")
# Handle the failure
# Return 200 to acknowledge receipt
return jsonify({"received": True}), 200
if __name__ == '__main__':
app.run(host='0.0.0.0', port=443, ssl_context='adhoc')
Node.js (Express)
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhook/generation-complete', (req, res) => {
const { data } = req.body;
const { task_id, status, files, error_message } = data;
if (status === 'finished') {
// Task completed successfully
console.log(`Task ${task_id} completed`);
files.forEach(file => {
console.log(`Generated file: ${file.file_url}`);
// Download and process the file
});
} else if (status === 'failed') {
// Task failed
console.log(`Task ${task_id} failed: ${error_message}`);
// Handle the failure
}
// Return 200 to acknowledge receipt
res.status(200).json({ received: true });
});
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('private-key.pem'),
cert: fs.readFileSync('certificate.pem')
};
https.createServer(options, app).listen(443, () => {
console.log('Webhook server listening on port 443');
});
package main
import (
"encoding/json"
"fmt"
"net/http"
)
type CallbackPayload struct {
Code int `json:"code"`
Data TaskData `json:"data"`
}
type TaskData struct {
TaskID string `json:"task_id"`
Status string `json:"status"`
Files []MediaFile `json:"files"`
CreatedTime string `json:"created_time"`
Progress int `json:"progress"`
ErrorMessage *string `json:"error_message"`
}
type MediaFile struct {
FileURL string `json:"file_url"`
FileType string `json:"file_type"`
}
func handleWebhook(w http.ResponseWriter, r *http.Request) {
var payload CallbackPayload
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
taskData := payload.Data
if taskData.Status == "finished" {
// Task completed successfully
fmt.Printf("Task %s completed\n", taskData.TaskID)
for _, file := range taskData.Files {
fmt.Printf("Generated file: %s\n", file.FileURL)
// Download and process the file
}
} else if taskData.Status == "failed" {
// Task failed
fmt.Printf("Task %s failed: %s\n", taskData.TaskID, *taskData.ErrorMessage)
// Handle the failure
}
// Return 200 to acknowledge receipt
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]bool{"received": true})
}
func main() {
http.HandleFunc("/webhook/generation-complete", handleWebhook)
// Use HTTPS
err := http.ListenAndServeTLS(":443", "cert.pem", "key.pem", nil)
if err != nil {
panic(err)
}
}
Security Best Practices
Verify requests: Consider adding a signature verification mechanism to ensure requests are from Vidgo API
Idempotency: Design your webhook handler to be idempotent in case of duplicate deliveries
Async processing: Process the callback asynchronously and return 200 quickly to avoid timeouts
Logging: Log all webhook requests for debugging and monitoring
Testing
Using ngrok for Local Testing
During development, you can use ngrok to expose your local server:
# Start your local webhook server on port 3000
node webhook-server.js
# In another terminal, start ngrok
ngrok http 3000
# Use the HTTPS URL provided by ngrok as your callback_url
# Example: https://abc123.ngrok.io/webhook/generation-complete
Webhook vs Polling
| Method | Best For | Pros | Cons |
|---|
| Webhooks | Production systems | Real-time notifications, no polling overhead | Requires public endpoint, harder to debug |
| Polling | Development, simple integrations | Easy to implement, no server required | Higher latency, consumes more resources |
For production systems handling high volumes, webhooks are recommended to reduce API calls and get instant notifications.
Next Steps