Overview
Webhooks allow you to receive HTTP notifications when jobs complete, eliminating the need for polling.Setup
Include awebhook_url in your job request:
Webhook Payload
When a job completes, Tornado sends aPOST request to your webhook_url with a JSON payload. Here’s what your server will receive:
Single Job Completion
| Field | Type | Description |
|---|---|---|
type | string | Always "job_completed" |
job_id | string | UUID of the job |
status | string | "completed" |
url | string | Original source URL |
key | string | Storage key of the uploaded file |
folder | string | null | Folder prefix if provided at job creation |
title | string | null | Video/episode title. Available for YouTube and Spotify |
description | string | null | Content description from YouTube. null for Spotify |
release_date | string | null | ISO 8601 date. Spotify: exact publish date. YouTube: upload date (midnight UTC) |
subtitle_key | string | null | Storage key for subtitles (if download_subtitles was enabled) |
Job Failed
Sent when a job fails due to a technical error (bot detection, rate limit, connection issues). Not sent for content warnings (private/members-only/geo-blocked videos) — those are handled silently.| Field | Type | Description |
|---|---|---|
type | string | Always "job_failed" |
job_id | string | UUID of the job |
status | string | "failed" |
url | string | Original source URL |
error | string | Error message |
Job Skipped
Sent when a job is skipped because the content cannot be processed. This happens for audio-only Spotify episodes that are protected by Widevine DRM and have no video stream available. Skipped jobs are classified as warnings, not errors — they represent content limitations, not technical failures.| Field | Type | Description |
|---|---|---|
type | string | Always "job_skipped" |
job_id | string | UUID of the job |
status | string | Always "skipped" |
url | string | Original source URL |
reason | string | Explanation of why the job was skipped |
In batch downloads, skipped episodes count toward the batch
failed counter for completion tracking purposes. The batch webhook fires once all episodes are either completed, failed, or skipped.Batch Completion
When all episodes in a batch are done:| Field | Type | Description |
|---|---|---|
type | string | Always "batch_completed" |
batch_id | string | UUID of the batch |
status | string | "completed" (all succeeded) or "finished" (some failed) |
show_url | string | Original Spotify show URL |
folder | string | null | Folder prefix if provided |
total_episodes | integer | Total episodes in the batch |
completed_episodes | integer | Successfully completed episodes |
failed_episodes | integer | Failed/skipped episodes |
Progress Webhooks
Whenenable_progress_webhook: true is set, you’ll receive updates at each processing stage:
| Stage | Progress | Description |
|---|---|---|
downloading | 0% | Download started |
download_complete | 33% | Download finished |
muxing | 33% | Muxing audio/video started |
mux_complete | 66% | Muxing finished |
uploading | 66% | Upload to storage started |
completed | 100% | Job completed (standard webhook) |
Webhook Behavior
| Aspect | Behavior |
|---|---|
| Method | POST |
| Content-Type | application/json |
| Timeout | 30 seconds |
| Retries | 3 attempts with exponential backoff |
Handling Webhooks
Node.js (Express)
Python (Flask)
Best Practices
Respond Quickly
Respond Quickly
Return a 2xx status code within 30 seconds. Process the webhook data asynchronously if needed.
Verify Source
Verify Source
Webhooks come from our servers. Consider IP whitelisting or signature verification for production.
Idempotency
Idempotency
Handle duplicate webhooks gracefully. Store the job_id and check for duplicates before processing.
Retry Logic
Retry Logic
If your endpoint fails, we retry 3 times. Ensure your handler can process the same event multiple times.
