Skip to main content

Overview

Webhooks allow you to receive HTTP notifications when jobs complete, eliminating the need for polling.

Setup

Include a webhook_url in your job request:
curl -X POST "https://tornado.velys.software/jobs" \
  -H "x-api-key: sk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://youtube.com/watch?v=...",
    "webhook_url": "https://your-app.com/webhooks/tornado"
  }'

Webhook Payload

When a job completes, Tornado sends a POST request to your webhook_url with a JSON payload. Here’s what your server will receive:

Single Job Completion

{
  "type": "job_completed",
  "job_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "Completed",
  "url": "https://youtube.com/watch?v=...",
  "s3_key": "videos/my-video.mp4",
  "s3_url": "https://cdn.example.com/videos/my-video.mp4?signature=...",
  "file_size_bytes": 104857600
}

Job Failed

{
  "type": "job_completed",
  "job_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "Failed",
  "url": "https://youtube.com/watch?v=...",
  "error": "Video unavailable"
}

Batch Completion

When all episodes in a batch are done:
{
  "type": "batch_completed",
  "batch_id": "550e8400-e29b-41d4-a716-446655440001",
  "show_url": "https://open.spotify.com/show/...",
  "folder": "my-podcast-2024",
  "completed": 140,
  "failed": 2,
  "total": 142
}

Webhook Behavior

AspectBehavior
MethodPOST
Content-Typeapplication/json
Timeout30 seconds
Retries3 attempts with exponential backoff

Handling Webhooks

Node.js (Express)

const express = require('express');
const app = express();

app.use(express.json());

app.post('/webhooks/tornado', (req, res) => {
  const { type, job_id, status, s3_url, error } = req.body;

  if (type === 'job_completed') {
    if (status === 'Completed') {
      console.log(`Job ${job_id} completed: ${s3_url}`);
      // Download or process the file
    } else {
      console.log(`Job ${job_id} failed: ${error}`);
      // Handle error
    }
  } else if (type === 'batch_completed') {
    const { batch_id, completed, failed, total } = req.body;
    console.log(`Batch ${batch_id}: ${completed}/${total} (${failed} failed)`);
  }

  res.status(200).send('OK');
});

app.listen(3000);

Python (Flask)

from flask import Flask, request

app = Flask(__name__)

@app.route('/webhooks/tornado', methods=['POST'])
def handle_webhook():
    data = request.json

    if data['type'] == 'job_completed':
        if data['status'] == 'Completed':
            print(f"Job {data['job_id']} completed: {data['s3_url']}")
        else:
            print(f"Job {data['job_id']} failed: {data['error']}")

    elif data['type'] == 'batch_completed':
        print(f"Batch {data['batch_id']}: {data['completed']}/{data['total']}")

    return 'OK', 200

if __name__ == '__main__':
    app.run(port=3000)

Best Practices

Return a 2xx status code within 30 seconds. Process the webhook data asynchronously if needed.
Webhooks come from our servers. Consider IP whitelisting or signature verification for production.
Handle duplicate webhooks gracefully. Store the job_id and check for duplicates before processing.
If your endpoint fails, we retry 3 times. Ensure your handler can process the same event multiple times.

Testing Webhooks

Use a service like webhook.site or ngrok to test webhooks locally:
# Start ngrok tunnel
ngrok http 3000

# Use the ngrok URL as your webhook_url
# https://abc123.ngrok.io/webhooks/tornado