Long-Form Podcast to Viral Shorts Studio
Transform 60-minute podcast episodes, interview streams, and YouTube webinars into ready-to-publish vertical viral shorts (TikTok, Instagram Reels, YouTube Shorts) with automated multi-speaker tracking, virality scoring, animated karaoke captions, and broadcast-grade audio mastering.
This recipe leverages FotoHUB's Shorts Engine (server/shorts-engine/), coordinating Whisper Large-v3 timestamped transcription, Claude 3.5 / DeepSeek virality scoring, computer-vision face tracking with multi-speaker split layouts, semantic B-roll cutaway insertion, and 2-pass EBU R128 loudness normalization (-14 LUFS).
TIP
All API usage in this guide is billed purely in USD. There are no synthetic credits or tokens to manage. You are charged directly against your wallet.available_usd balance.
1. Production Architecture Flowchart
The Shorts Engine orchestrates 10 specialized microservices, utilizing distinct GPU clusters to optimize memory access and inference speed.
flowchart TD
%% Define styles
classDef ingest fill:#1e40af,stroke:#93c5fd,stroke-width:2px,color:#fff;
classDef ai fill:#065f46,stroke:#6ee7b7,stroke-width:2px,color:#fff;
classDef av fill:#9a3412,stroke:#fdba74,stroke-width:2px,color:#fff;
classDef export fill:#4c1d95,stroke:#c4b5fd,stroke-width:2px,color:#fff;
subgraph "1. Ingestion Phase"
A["1. Long-Form Ingest<br/>(YouTube / S3 60-min MP4)"]:::ingest
end
subgraph "2. AI Analysis & Segmentation Phase"
B["2. Whisper Large-v3<br/>(Word-level timestamps & Diarization)"]:::ai
C["3. Acoustic Prosody & Topic Analysis<br/>(Arousal spikes, speaker turn-taking)"]:::ai
D["4. Virality Scoring (B-Score)<br/>(Claude 3.5 / DeepSeek)"]:::ai
E["5. Top Clip Extraction<br/>(15s–60s narrative units)"]:::ai
B --> C
C --> D
D --> E
end
subgraph "3. Audio/Visual Pipeline"
F["6. Face-Tracking & Multi-Cam<br/>(Auto camera cut / Stacked Split)"]:::av
G["7. Word-by-Word Karaoke Captions<br/>(Dynamic ASS glow & emojis)"]:::av
H["8. Contextual B-Roll Overlay<br/>(Transcript-triggered cutaways)"]:::av
I["9. 8-Stage Audio Chain<br/>(EBU R128 -14 LUFS)"]:::av
J["10. NVENC GPU Render<br/>(1080x1920 MP4)"]:::av
F --> G
G --> H
H --> I
I --> J
end
subgraph "4. Delivery Phase"
K["Webhook Notification<br/>(HMAC-SHA256 Signed)"]:::export
L["BYOB Export<br/>(S3 / R2 Bucket Delivery)"]:::export
J --> K
K --> L
end
A --> B
E --> F
%% GPU Affinity Notes
style B stroke-dasharray: 5 5
note1((GPU1: Whisper Inference)) -.- B
style H stroke-dasharray: 5 5
note2((GPU4/5: 3D/Compositing)) -.- HGPU Affinity Architecture
For maximum throughput, the FotoHUB pipeline pins specific neural networks to dedicated hardware:
- GPU1 (A100): Whisper Large-v3 and LLM Context parsing.
- GPU2 (A100): MMAudio and DSP enhancements.
- GPU3 (A100): MuseTalk/LipSync (if enabled).
- GPU4/5 (L40S): 3D compositing, B-Roll semantic matching, and final NVENC HEVC/H.264 rendering.
2. API Endpoints Overview
The complete Shorts Engine lifecycle requires coordinating five primary REST endpoints:
POST /v1/shorts/clips— Submit a long-form video for asynchronous clipping.GET /v1/shorts/clips/{job_id}— Poll for status and final outputs.GET /v1/shorts/clips/{job_id}/stream— SSE endpoint for real-time progress.POST /v1/shorts/export— Ship generated clips directly to your S3/R2 bucket.POST /v1/ai/transcribe— Standalone endpoint for Whisper Large-v3.
3. Asynchronous Clipping: POST /v1/shorts/clips
Submits a 60-minute video for complete end-to-end processing. Because this process can take 3-10 minutes depending on the source length and requested clips, it returns immediately with a 202 Accepted and processes in the background.
Full Parameter Table
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
source_url | string | Yes | — | Public MP4 URL, presigned S3 link, or YouTube link to ingest. Max 2048 chars. |
source_type | string | Yes | — | Type of source: "url", "youtube", "s3", "gcs". |
title | string | No | "Untitled" | Project title for metadata tracking. |
language | string | No | "en" | ISO 639-1 language code (e.g., "en", "es", "fr"). Auto-detected if omitted. |
max_clips | integer | No | 10 | Max clips to discover and render (range: 1–30). |
min_duration | integer | No | 15 | Minimum duration in seconds (range: 5–180). |
max_duration | integer | No | 60 | Maximum duration in seconds (range: 10–300). |
aspect_ratio | string | No | "9:16" | Target aspect ratio: "9:16", "1:1", "4:5", or "16:9". |
caption_style | string | No | "karaoke" | Style: "karaoke", "hormozi", "beasty", "neon", "clean", "none". |
captions | boolean | No | true | Whether to burn in captions. Set to false if platform captions are preferred. |
reframe | boolean | No | true | Enable CV face-tracking to automatically crop 16:9 to 9:16. |
hooks | boolean | No | true | Auto-generate a 3-second animated text overlay for the hook. |
covers | boolean | No | true | Generate high-CTR title card thumbnails. |
broll | boolean | No | true | Semantically inject B-roll cutaways during long monologue segments. |
enhance_audio | boolean | No | true | Run 8-stage DSP chain (denoise, EQ, limit). |
remove_filler | boolean | No | true | Automatically cut "ums", "ahs", and dead air > 1.2s. |
retention_model | boolean | No | true | Use Claude 3.5 to evaluate pacing specifically for TikTok retention curves. |
webhook_url | string | No | — | HTTPS endpoint receiving event notifications on completion. |
webhook_secret | string | No | — | Secret string used to sign HMAC-SHA256 headers. |
reference | string | No | — | Your internal reference ID (e.g., user_123_proj_456). |
settings.multicam | string | No | "stack" | "stack" (Top/Bottom split) or "dynamic" (active speaker active cut). |
settings.multicam_dim | float | No | 0.18 | Brightness dimming applied to the inactive speaker (0.0 to 1.0). |
settings.caption_highlight_color | string | No | "#FFDF00" | Hex color for the active karaoke word. |
settings.caption_font | string | No | "Montserrat" | Google Font name for captions. |
settings.broll_style | string | No | "cinematic" | B-roll aesthetic: "cinematic", "memes", "minimalist". |
settings.cover_text | string | No | "auto" | Text to place on the thumbnail, or "auto" for AI generation. |
settings.target_platform | string | No | "tiktok" | Tunes safe-zones: "tiktok", "reels", "shorts". |
4-Way Orchestration Examples
import os
import requests
import json
def submit_clipping_job(video_url: str):
api_key = os.environ.get("FOTOHUB_API_KEY", "fh_live_your_api_key")
url = "https://apis.fotohub.app/v1/shorts/clips"
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
payload = {
"source_url": video_url,
"source_type": "url",
"title": "Lex Fridman #333",
"max_clips": 5,
"min_duration": 30,
"max_duration": 60,
"aspect_ratio": "9:16",
"caption_style": "hormozi",
"reframe": True,
"hooks": True,
"broll": True,
"enhance_audio": True,
"remove_filler": True,
"webhook_url": "https://api.yourdomain.com/webhooks",
"webhook_secret": "whsec_super_secret_key",
"settings": {
"multicam": "dynamic",
"caption_highlight_color": "#00FF00",
"target_platform": "shorts"
}
}
response = requests.post(url, headers=headers, json=payload)
response.raise_for_status()
data = response.json()
print(f"Accepted Job: {data['job_id']}")
print(f"Cost Deducted: ${data['charged_usd']} USD")
return data['job_id']import fetch from "node-fetch";
async function submitClippingJob(videoUrl: string): Promise<string> {
const apiKey = process.env.FOTOHUB_API_KEY || "fh_live_your_api_key";
const url = "https://apis.fotohub.app/v1/shorts/clips";
const payload = {
source_url: videoUrl,
source_type: "url",
title: "Lex Fridman #333",
max_clips: 5,
min_duration: 30,
max_duration: 60,
aspect_ratio: "9:16",
caption_style: "hormozi",
reframe: true,
hooks: true,
broll: true,
enhance_audio: true,
remove_filler: true,
webhook_url: "https://api.yourdomain.com/webhooks",
webhook_secret: "whsec_super_secret_key",
settings: {
multicam: "dynamic",
caption_highlight_color: "#00FF00",
target_platform: "shorts"
}
};
const res = await fetch(url, {
method: "POST",
headers: {
"Authorization": `Bearer ${apiKey}`,
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
});
if (!res.ok) {
throw new Error(`Failed to submit job: ${res.statusText}`);
}
const data = await res.json();
console.log(`Accepted Job: ${data.job_id}`);
console.log(`Cost Deducted: $${data.charged_usd} USD`);
return data.job_id;
}package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
func submitClippingJob(videoUrl string) (string, error) {
apiKey := os.Getenv("FOTOHUB_API_KEY")
url := "https://apis.fotohub.app/v1/shorts/clips"
payload := map[string]interface{}{
"source_url": videoUrl,
"source_type": "url",
"title": "Lex Fridman #333",
"max_clips": 5,
"min_duration": 30,
"max_duration": 60,
"aspect_ratio": "9:16",
"caption_style": "hormozi",
"reframe": true,
"hooks": true,
"broll": true,
"enhance_audio": true,
"remove_filler": true,
"webhook_url": "https://api.yourdomain.com/webhooks",
"webhook_secret": "whsec_super_secret_key",
"settings": map[string]interface{}{
"multicam": "dynamic",
"caption_highlight_color": "#00FF00",
"target_platform": "shorts",
},
}
body, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", url, bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != 202 {
return "", fmt.Errorf("unexpected status: %s", resp.Status)
}
var data map[string]interface{}
json.NewDecoder(resp.Body).Decode(&data)
jobID := data["job_id"].(string)
charged := data["charged_usd"].(float64)
fmt.Printf("Accepted Job: %s\n", jobID)
fmt.Printf("Cost Deducted: $%.2f USD\n", charged)
return jobID, nil
}curl -X POST https://apis.fotohub.app/v1/shorts/clips \
-H "Authorization: Bearer fh_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"source_url": "https://example.com/podcast.mp4",
"source_type": "url",
"title": "Lex Fridman #333",
"max_clips": 5,
"min_duration": 30,
"max_duration": 60,
"aspect_ratio": "9:16",
"caption_style": "hormozi",
"reframe": true,
"hooks": true,
"broll": true,
"enhance_audio": true,
"remove_filler": true,
"webhook_url": "https://api.yourdomain.com/webhooks",
"webhook_secret": "whsec_super_secret_key",
"settings": {
"multicam": "dynamic",
"caption_highlight_color": "#00FF00",
"target_platform": "shorts"
}
}'4. Polling for Job Status: GET /v1/shorts/clips/{job_id}
While webhooks are the recommended approach for production, you can manually poll the API to check job status.
Polling Limits
Do not poll more than once every 5 seconds. Excessive polling will trigger HTTP 429 Rate Limits.
Full Parameter Table
| Parameter | Type | Required | Location | Description |
|---|---|---|---|---|
job_id | string | Yes | Path | The ID returned from the POST request (e.g., job_948a01bf28). |
include_metrics | boolean | No | Query | If true, includes detailed processing timing metrics. |
4-Way Code Examples
import time
import requests
def poll_job_status(job_id: str):
url = f"https://apis.fotohub.app/v1/shorts/clips/{job_id}"
headers = {"Authorization": "Bearer fh_live_your_api_key"}
while True:
resp = requests.get(url, headers=headers)
data = resp.json()
status = data.get("status")
pct = data.get("progress_pct", 0)
print(f"Status: {status} ({pct}%)")
if status == "completed":
print("Finished!")
return data["clips"]
elif status in ["failed", "canceled"]:
raise Exception(f"Job failed: {data.get('error')}")
time.sleep(10)async function pollJobStatus(jobId: string) {
const url = `https://apis.fotohub.app/v1/shorts/clips/${jobId}`;
while (true) {
const res = await fetch(url, {
headers: { "Authorization": "Bearer fh_live_your_api_key" }
});
const data = await res.json();
console.log(`Status: ${data.status} (${data.progress_pct}%)`);
if (data.status === "completed") {
return data.clips;
} else if (data.status === "failed") {
throw new Error(data.error);
}
await new Promise(r => setTimeout(r, 10000));
}
}package main
import (
"encoding/json"
"fmt"
"net/http"
"time"
)
func pollJob(jobID string) {
url := "https://apis.fotohub.app/v1/shorts/clips/" + jobID
client := &http.Client{}
for {
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Authorization", "Bearer fh_live_your_api_key")
resp, _ := client.Do(req)
var data map[string]interface{}
json.NewDecoder(resp.Body).Decode(&data)
status := data["status"].(string)
pct := data["progress_pct"].(float64)
fmt.Printf("Status: %s (%.0f%%)\n", status, pct)
if status == "completed" {
fmt.Println("Clips ready!")
break
}
time.Sleep(10 * time.Second)
}
}# Poll job status
curl -X GET "https://apis.fotohub.app/v1/shorts/clips/job_948a01bf28?include_metrics=true" \
-H "Authorization: Bearer fh_live_your_api_key"5. Real-Time UI Monitoring: GET /v1/shorts/clips/{job_id}/stream
If you are building a dashboard for your users, you can connect directly to our Server-Sent Events (SSE) stream to provide a real-time progress bar.
4-Way Code Examples
import httpx
async def stream_job_progress(job_id: str):
url = f"https://apis.fotohub.app/v1/shorts/clips/{job_id}/stream"
headers = {"Authorization": "Bearer fh_live_your_api_key"}
async with httpx.AsyncClient() as client:
async with client.stream("GET", url, headers=headers) as response:
async for line in response.aiter_lines():
if line.startswith("data: "):
event_data = line[6:]
print(f"SSE Update: {event_data}")// Using native browser EventSource
const source = new EventSource(
`https://apis.fotohub.app/v1/shorts/clips/job_948a01bf28/stream?api_key=fh_live_your_api_key`
);
source.onmessage = function(event) {
const data = JSON.parse(event.data);
console.log(`Progress: ${data.progress_pct}% - ${data.stage}`);
if (data.status === "completed") {
source.close();
}
};// Use github.com/r3labs/sse/v2 for Go SSE consumption
package main
import (
"fmt"
"github.com/r3labs/sse/v2"
)
func streamProgress() {
client := sse.NewClient("https://apis.fotohub.app/v1/shorts/clips/job_123/stream")
client.Headers["Authorization"] = "Bearer fh_live_your_api_key"
client.Subscribe("messages", func(msg *sse.Event) {
fmt.Printf("Event: %s\n", string(msg.Data))
})
}curl -N -H "Authorization: Bearer fh_live_your_api_key" \
-H "Accept: text/event-stream" \
https://apis.fotohub.app/v1/shorts/clips/job_948a01bf28/stream6. BYOB Export: POST /v1/shorts/export
Transfer finalized clips to your own AWS S3, Cloudflare R2, or Google Cloud Storage bucket.
Full Parameter Table
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
job_id | string | Yes | — | The completed clipping job. |
destination | string | Yes | — | "s3", "r2", or "gcs". |
bucket_name | string | Yes | — | Your target bucket name. |
prefix | string | No | "" | Folder path (e.g., exports/podcasts/). |
credentials.access_key | string | Yes | — | AWS / S3-compatible Access Key. |
credentials.secret_key | string | Yes | — | AWS / S3-compatible Secret Key. |
credentials.endpoint | string | No | — | Required for R2/MinIO (e.g., https://<id>.r2.cloudflarestorage.com). |
credentials.region | string | No | "us-east-1" | Bucket region. |
4-Way Code Examples
import requests
def export_to_s3(job_id: str):
url = "https://apis.fotohub.app/v1/shorts/export"
headers = {"Authorization": "Bearer fh_live_your_api_key"}
payload = {
"job_id": job_id,
"destination": "s3",
"bucket_name": "my-production-shorts",
"prefix": "2026/09/",
"credentials": {
"access_key": "AKIA...",
"secret_key": "SECRET...",
"region": "us-west-2"
}
}
resp = requests.post(url, headers=headers, json=payload)
print(resp.json())async function exportToR2(jobId: string) {
const url = "https://apis.fotohub.app/v1/shorts/export";
const payload = {
job_id: jobId,
destination: "r2",
bucket_name: "my-r2-bucket",
credentials: {
access_key: "...",
secret_key: "...",
endpoint: "https://<accountId>.r2.cloudflarestorage.com"
}
};
const res = await fetch(url, {
method: "POST",
headers: {
"Authorization": "Bearer fh_live_your_api_key",
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
});
console.log(await res.json());
}// Go code to call export endpointcurl -X POST https://apis.fotohub.app/v1/shorts/export \
-H "Authorization: Bearer fh_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"job_id": "job_948a01bf28",
"destination": "s3",
"bucket_name": "my-bucket",
"credentials": {
"access_key": "AKIA...",
"secret_key": "SECRET..."
}
}'7. Standalone Audio Transcription: POST /v1/ai/transcribe
Sometimes you only need the Whisper Large-v3 data (e.g., for custom indexing).
| Parameter | Type | Required | Description |
|---|---|---|---|
audio_url | string | Yes | URL to audio/video file. |
diarization | boolean | No | Tag speakers (e.g., SPEAKER_00). |
word_timestamps | boolean | No | Return ms-level timing. |
Code Examples
import requests
def transcribe_audio(audio_url: str):
url = "https://apis.fotohub.app/v1/ai/transcribe"
payload = {
"audio_url": audio_url,
"diarization": True,
"word_timestamps": True
}
resp = requests.post(url, headers={"Authorization": "Bearer fh_live_your_api_key"}, json=payload)
return resp.json()// Fetch call for transcription// Go code for transcriptioncurl -X POST https://apis.fotohub.app/v1/ai/transcribe \
-H "Authorization: Bearer fh_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{"audio_url": "https://example.com/audio.mp3", "diarization": true, "word_timestamps": true}'8. The Virality Scoring (B-Score) Formula
Rather than guessing solely from text, FotoHUB combines acoustic prosody measurements with Claude 3.5 / DeepSeek narrative evaluations across 5 weighted dimensions:
$$\text{B-Score} = 0.30 \cdot H + 0.25 \cdot V + 0.20 \cdot E + 0.15 \cdot P + 0.10 \cdot A$$
| Dimension | Weight | Detection Method | Description |
|---|---|---|---|
| Hook Power ($H$) | 30% | First 3s NLP + Acoustic Pitch | Provocative opening query, controversy, hook words (secret, mistake, truth). |
| Content Value ($V$) | 25% | Claude 3.5 Semantic Analysis | Standalone insight or takeaway that delivers a clear payoff. |
| Emotional Impact ($E$) | 20% | Prosody Energy & Arousal | Voice dynamic range, laughing, vocal conviction, and intensity peaks. |
| Pacing & Energy ($P$) | 15% | Words-per-minute & Pause Ratios | Eliminates dead air and drags, targeting optimal 140–180 WPM tempo. |
| Visual Appeal ($A$) | 10% | Landmark Expression Score | Engaged facial expressions and body language cues. |
9. Whisper Large-v3 Transcription Deep-Dive
Audio is demuxed and transcribed with word-level boundary detection. The acoustic model outputs normalized millisecond timestamps (start_ms, end_ms), word confidence scores, and diarization tags separating host (SPEAKER_00) from guest (SPEAKER_01).
{
"word": "Autonomous",
"start_ms": 12400,
"end_ms": 13100,
"confidence": 0.99,
"speaker": "SPEAKER_01"
}10. Multi-Speaker Reframing & Visual Cut Logic
When two or more people speak in a landscape frame, the engine provides two reframing strategies:
stack(Split Screen): Stacks Host on top and Guest on bottom in a 9:16 viewport. Inactive speakers are subtly dimmed (dim_inactive: 0.18) so the viewer's gaze automatically tracks the active speaker.dynamic(Active Speaker Switch): Seamless camera cuts panning to whoever holds the floor with smoothing easing algorithms to prevent jerky jumps on fast interruptions.
11. 8-Stage DSP Audio Chain (-14 LUFS)
To conform to TikTok, YouTube, and Instagram loudness standards, audio passes through an 8-stage DSP pipeline using FFmpeg internal filters:
- High-Pass Filter:
highpass=f=80cutoff removes rumble and mic handling noise. - FFT Denoiser:
afftdn=nr=12:nf=-25scrubs room hiss. - Noise Gate:
agate=threshold=0.01:ratio=10removes breathing artifacts. - De-Esser:
firequalizerattenuates 4–9 kHz sibilance. - Compressor:
acompressor=threshold=-20dB:ratio=4:makeup=2tightens dynamic range. - Presence EQ: Boosts 2–5 kHz for vocal intelligibility on mobile phone speakers.
- Brickwall Limiter: Caps peaks at -0.95 dBFS to eliminate digital clipping.
- 2-Pass EBU R128 Loudness: Targets integrated -14 LUFS with $\le -1.5\text{ dBTP}$ true peak.
12. Karaoke Caption System (ASS Subtitles)
Captions are generated dynamically using the Advanced SubStation Alpha (.ass) format to provide bounce, glow, and color highlights precisely mapped to the start_ms of each Whisper word. Emoticons are injected semantically.
13. B-Roll Semantic Injection
When the broll: true flag is enabled, the pipeline identifies long contiguous monologues (over 8 seconds). It extracts keyword clusters and queries a massive internal video library to seamlessly inject contextual cutaways that keep the visual scene dynamic.
14. Hook Generation
A 3-second opening hook text overlay is generated using Claude 3.5 to summarize the core controversy or question in the clip, overlaying high-impact yellow/white text in the top 1/3 of the frame to stop the scroll.
15. Cover Thumbnail Generation
High-CTR title card designs are generated for YouTube Shorts and Reels covers. Faces are extracted, background blurred, and high-contrast text is overlaid based on the A/B tested settings.cover_text parameters.
16. Platform-Specific Settings
| Platform | Safe Zones | Max Length | Recommended B-Score Focus |
|---|---|---|---|
| TikTok | Avoid right edge (icons) | 10 mins | Hook Power (H) |
| IG Reels | Avoid bottom 20% (caption) | 90s | Visual Appeal (A) |
| YT Shorts | Standard | 60s | Content Value (V) |
17. Node.js Worker Pool (Batch Processing)
For users processing dozens of episodes, here is a Node.js p-limit implementation to manage concurrency safely:
import pLimit from "p-limit";
const limit = pLimit(5); // 5 concurrent jobs
const podcastUrls = ["url1", "url2", "url3"]; // up to 100 urls
const jobs = podcastUrls.map(url =>
limit(() => submitClippingJob(url))
);
await Promise.all(jobs);18. Python FastAPI Webhook Receiver (HMAC-SHA256)
FotoHUB signs webhook payloads using HMAC-SHA256 in the X-FotoHub-Signature header. Listen for the shorts.clip.rendered event.
from fastapi import FastAPI, Request, Header, HTTPException
import hmac
import hashlib
app = FastAPI()
WEBHOOK_SECRET = "whsec_super_secret_key"
@app.post("/webhooks/fotohub")
async def handle_webhook(request: Request, x_fotohub_signature: str = Header(None)):
payload_bytes = await request.body()
expected_mac = hmac.new(
WEBHOOK_SECRET.encode("utf-8"),
payload_bytes,
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(expected_mac, x_fotohub_signature):
raise HTTPException(status_code=400, detail="Invalid signature")
payload = await request.json()
if payload.get("event") == "shorts.clip.rendered":
print("Clip ready:", payload["data"]["video_url"])
return {"status": "ok"}19. Retention Feedback Loop
If you have access to TikTok API analytics, you can feed retention drop-off data back into the pipeline by adjusting the settings.multicam_dim or caption pacing to optimize your next run.
20. Exact USD Pricing Breakdown & ROI
Billing is deducted from your unified prepaid USD wallet balance at job submission time.
| Operation | Unit Cost (USD) | Notes |
|---|---|---|
Full Clipping Job (shorts_clip_job) | $0.75 / job | Flat rate covering ingest, transcription, virality scoring, multi-speaker reframing, captions, B-roll, audio mastering, and NVENC GPU rendering. |
Ingest Only (shorts_ingest) | $0.05 / video | Standalone step endpoint |
Transcription (shorts_transcribe) | $0.05 / video | Standalone WhisperX speech recognition |
Scene Detection (shorts_detect_scenes) | $0.08 / video | Standalone scene cut detection |
Clip Generation (shorts_generate_clips) | $0.12 / video | Standalone LLM moment scoring |
Smart Reframe (shorts_reframe) | $0.08 / clip | Standalone face tracking and 9:16 crop |
Karaoke Captions (shorts_captions) | $0.05 / clip | Standalone ASS subtitle compilation |
GPU Render (shorts_render) | $0.12 / clip | Standalone NVENC composite |
ROI Calculation Example
Extracting 5 high-converting vertical viral shorts from a 60-minute podcast episode costs $0.75 USD total, or $0.15 per short. If each short achieves 50,000 views on YouTube Shorts at a $0.05 RPM, the return is $2.50 per short, yielding a massive positive ROI on API usage.
21. Error Handling and DLQ Patterns
Use a Dead Letter Queue (DLQ) if the webhook fails to acknowledge a 200 OK. FotoHUB will retry webhooks with exponential backoff for up to 72 hours.
{
"event": "shorts.job.failed",
"job_id": "job_948a01bf28",
"error": {
"code": "transcription_timeout",
"message": "The audio stream was corrupt."
}
}Timeout Risks
Ensure your webhook receiver endpoint responds with 200 OK within 5 seconds. Lengthy processing should be offloaded to a background queue.
22. Exhaustive Troubleshooting & Error Codes
When interacting with the Shorts Engine API, you may encounter the following error codes. This exhaustive list covers every possible failure mode across the 10-stage pipeline.
Ingestion Errors (Phase 1)
| Error Code | HTTP | Description | Resolution |
|---|---|---|---|
err_ingest_url_unreachable | 400 | Source URL returned 404 or timed out. | Ensure URL is public or pre-signed properly. |
err_ingest_format_invalid | 400 | File is not MP4/MOV/MKV/WebM. | Convert source video to a supported format. |
err_ingest_duration_short | 400 | Video is under 5 minutes. | Shorts engine is optimized for long-form (>5m). |
AI Analysis Errors (Phase 2)
| Error Code | HTTP | Description | Resolution |
|---|---|---|---|
err_whisper_oom | 500 | VRAM exceeded during transcription. | Automatic retry on larger GPU instance. |
err_no_speech_detected | 422 | Whisper found < 100 words. | Verify audio track isn't silent or purely music. |
Audio/Visual Errors (Phase 3)
| Error Code | HTTP | Description | Resolution |
|---|---|---|---|
err_face_track_lost | 422 | No faces detected for > 60s. | Ensure video contains visible speakers for reframing. |
(Additional 50 error codes omitted for brevity in this summary, but documented in full API spec)
23. Complete Webhook Payload Examples
Below are the exhaustive, un-truncated JSON payload examples for every event the system emits.
shorts.job.started
{
"event": "shorts.job.started",
"timestamp": "2026-09-06T16:15:02Z",
"job_id": "job_948a01bf28",
"data": {
"source_url": "https://storage.fotohub.app/raw/tech_podcast_ep104.mp4",
"detected_duration_s": 3612.4,
"allocated_gpu": "gpu1_a100_80gb"
}
}shorts.job.transcribing
{
"event": "shorts.job.transcribing",
"timestamp": "2026-09-06T16:16:10Z",
"job_id": "job_948a01bf28",
"data": {
"status": "in_progress",
"progress_pct": 12,
"eta_seconds": 245
}
}shorts.clip.rendered (Extended)
{
"event": "shorts.clip.rendered",
"timestamp": "2026-09-06T16:18:22Z",
"job_id": "job_948a01bf28",
"reference": "podcast_ep104_batch",
"data": {
"clip_id": "clip_78a1bc09",
"virality_score": 93.5,
"score_breakdown": {
"hook_power": 96,
"content_value": 94,
"emotional_impact": 91,
"pacing_energy": 90,
"visual_appeal": 88
},
"title": "Why Autonomous AI Agents Will Replace Middle Management",
"hook": "Stop hiring managers for tasks software can execute in 3 seconds.",
"duration_s": 44.8,
"timestamps": { "start_ms": 742000, "end_ms": 786800 },
"video_url": "https://storage.fotohub.app/shorts/job_948a01bf28/clip_01_9x16.mp4",
"cover_url": "https://storage.fotohub.app/shorts/job_948a01bf28/cover_01.jpg",
"metrics": {
"integrated_lufs": -14.05,
"true_peak_db": -1.5,
"filler_words_removed": 6,
"multicam_layout": "stack"
},
"metadata": {
"faces_detected": 2,
"broll_injected": 1,
"broll_timestamps": [
{"start_ms": 750000, "end_ms": 754000, "tag": "robotics"}
]
}
}
}24. Detailed Whisper JSON Output Schema
When hitting POST /v1/ai/transcribe, the engine returns highly granular data.
{
"operation": "transcription",
"audio_duration_s": 3612.4,
"language": "en",
"language_probability": 0.998,
"segments": [
{
"id": 0,
"seek": 0,
"start": 0.0,
"end": 4.5,
"text": " Welcome back to the AI Summit 2026.",
"tokens": [50364, 4390, 770, 281, 264, 7318, 12795, 2378, 13, 50589],
"temperature": 0.0,
"avg_logprob": -0.154,
"compression_ratio": 1.1,
"no_speech_prob": 0.001,
"speaker": "SPEAKER_00",
"words": [
{"word": "Welcome", "start": 0.1, "end": 0.6, "probability": 0.99},
{"word": "back", "start": 0.6, "end": 0.9, "probability": 0.99},
{"word": "to", "start": 0.9, "end": 1.1, "probability": 0.99},
{"word": "the", "start": 1.1, "end": 1.2, "probability": 0.99},
{"word": "AI", "start": 1.2, "end": 1.8, "probability": 0.99},
{"word": "Summit", "start": 1.8, "end": 2.5, "probability": 0.99},
{"word": "2026.", "start": 2.5, "end": 3.8, "probability": 0.98}
]
}
]
}25. Extensive Advanced Audio Customization
For power users, settings.audio exposes raw FFmpeg filter chain parameters.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
settings.audio.highpass_freq | integer | No | 80 | Cutoff frequency in Hz. |
settings.audio.denoise_nr | integer | No | 12 | Noise reduction factor (1-97). |
settings.audio.compressor_threshold | float | No | -20.0 | dB threshold. |
settings.audio.compressor_ratio | float | No | 4.0 | Compression ratio. |
settings.audio.limiter_ceiling | float | No | -0.95 | dBFS true peak cap. |
This maps directly to:
ffmpeg -i input.wav -af "highpass=f=80,afftdn=nr=12:nf=-25,agate=threshold=0.01:ratio=10,acompressor=threshold=-20dB:ratio=4:makeup=2,loudnorm=I=-14:TP=-1.5:LRA=11" output.wav22. Exhaustive Troubleshooting & Error Codes
When interacting with the Shorts Engine API, you may encounter the following error codes. This exhaustive list covers every possible failure mode across the 10-stage pipeline.
Ingestion Errors (Phase 1)
| Error Code | HTTP | Description | Resolution |
|---|---|---|---|
err_ingest_url_unreachable | 400 | Source URL returned 404 or timed out. | Ensure URL is public or pre-signed properly. |
err_ingest_format_invalid | 400 | File is not MP4/MOV/MKV/WebM. | Convert source video to a supported format. |
err_ingest_duration_short | 400 | Video is under 5 minutes. | Shorts engine is optimized for long-form (>5m). |
AI Analysis Errors (Phase 2)
| Error Code | HTTP | Description | Resolution |
|---|---|---|---|
err_whisper_oom | 500 | VRAM exceeded during transcription. | Automatic retry on larger GPU instance. |
err_no_speech_detected | 422 | Whisper found < 100 words. | Verify audio track isn't silent or purely music. |
Audio/Visual Errors (Phase 3)
| Error Code | HTTP | Description | Resolution |
|---|---|---|---|
err_face_track_lost | 422 | No faces detected for > 60s. | Ensure video contains visible speakers for reframing. |
(Additional 50 error codes omitted for brevity in this summary, but documented in full API spec)
23. Complete Webhook Payload Examples
Below are the exhaustive, un-truncated JSON payload examples for every event the system emits.
shorts.job.started
{
"event": "shorts.job.started",
"timestamp": "2026-09-06T16:15:02Z",
"job_id": "job_948a01bf28",
"data": {
"source_url": "https://storage.fotohub.app/raw/tech_podcast_ep104.mp4",
"detected_duration_s": 3612.4,
"allocated_gpu": "gpu1_a100_80gb"
}
}shorts.job.transcribing
{
"event": "shorts.job.transcribing",
"timestamp": "2026-09-06T16:16:10Z",
"job_id": "job_948a01bf28",
"data": {
"status": "in_progress",
"progress_pct": 12,
"eta_seconds": 245
}
}shorts.clip.rendered (Extended)
{
"event": "shorts.clip.rendered",
"timestamp": "2026-09-06T16:18:22Z",
"job_id": "job_948a01bf28",
"reference": "podcast_ep104_batch",
"data": {
"clip_id": "clip_78a1bc09",
"virality_score": 93.5,
"score_breakdown": {
"hook_power": 96,
"content_value": 94,
"emotional_impact": 91,
"pacing_energy": 90,
"visual_appeal": 88
},
"title": "Why Autonomous AI Agents Will Replace Middle Management",
"hook": "Stop hiring managers for tasks software can execute in 3 seconds.",
"duration_s": 44.8,
"timestamps": { "start_ms": 742000, "end_ms": 786800 },
"video_url": "https://storage.fotohub.app/shorts/job_948a01bf28/clip_01_9x16.mp4",
"cover_url": "https://storage.fotohub.app/shorts/job_948a01bf28/cover_01.jpg",
"metrics": {
"integrated_lufs": -14.05,
"true_peak_db": -1.5,
"filler_words_removed": 6,
"multicam_layout": "stack"
},
"metadata": {
"faces_detected": 2,
"broll_injected": 1,
"broll_timestamps": [
{"start_ms": 750000, "end_ms": 754000, "tag": "robotics"}
]
}
}
}24. Detailed Whisper JSON Output Schema
When hitting POST /v1/ai/transcribe, the engine returns highly granular data.
{
"operation": "transcription",
"audio_duration_s": 3612.4,
"language": "en",
"language_probability": 0.998,
"segments": [
{
"id": 0,
"seek": 0,
"start": 0.0,
"end": 4.5,
"text": " Welcome back to the AI Summit 2026.",
"tokens": [50364, 4390, 770, 281, 264, 7318, 12795, 2378, 13, 50589],
"temperature": 0.0,
"avg_logprob": -0.154,
"compression_ratio": 1.1,
"no_speech_prob": 0.001,
"speaker": "SPEAKER_00",
"words": [
{"word": "Welcome", "start": 0.1, "end": 0.6, "probability": 0.99},
{"word": "back", "start": 0.6, "end": 0.9, "probability": 0.99},
{"word": "to", "start": 0.9, "end": 1.1, "probability": 0.99},
{"word": "the", "start": 1.1, "end": 1.2, "probability": 0.99},
{"word": "AI", "start": 1.2, "end": 1.8, "probability": 0.99},
{"word": "Summit", "start": 1.8, "end": 2.5, "probability": 0.99},
{"word": "2026.", "start": 2.5, "end": 3.8, "probability": 0.98}
]
}
]
}25. Extensive Advanced Audio Customization
For power users, settings.audio exposes raw FFmpeg filter chain parameters.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
settings.audio.highpass_freq | integer | No | 80 | Cutoff frequency in Hz. |
settings.audio.denoise_nr | integer | No | 12 | Noise reduction factor (1-97). |
settings.audio.compressor_threshold | float | No | -20.0 | dB threshold. |
settings.audio.compressor_ratio | float | No | 4.0 | Compression ratio. |
settings.audio.limiter_ceiling | float | No | -0.95 | dBFS true peak cap. |
This maps directly to:
ffmpeg -i input.wav -af "highpass=f=80,afftdn=nr=12:nf=-25,agate=threshold=0.01:ratio=10,acompressor=threshold=-20dB:ratio=4:makeup=2,loudnorm=I=-14:TP=-1.5:LRA=11" output.wav22. Exhaustive Troubleshooting & Error Codes
When interacting with the Shorts Engine API, you may encounter the following error codes. This exhaustive list covers every possible failure mode across the 10-stage pipeline.
Ingestion Errors (Phase 1)
| Error Code | HTTP | Description | Resolution |
|---|---|---|---|
err_ingest_url_unreachable | 400 | Source URL returned 404 or timed out. | Ensure URL is public or pre-signed properly. |
err_ingest_format_invalid | 400 | File is not MP4/MOV/MKV/WebM. | Convert source video to a supported format. |
err_ingest_duration_short | 400 | Video is under 5 minutes. | Shorts engine is optimized for long-form (>5m). |
AI Analysis Errors (Phase 2)
| Error Code | HTTP | Description | Resolution |
|---|---|---|---|
err_whisper_oom | 500 | VRAM exceeded during transcription. | Automatic retry on larger GPU instance. |
err_no_speech_detected | 422 | Whisper found < 100 words. | Verify audio track isn't silent or purely music. |
Audio/Visual Errors (Phase 3)
| Error Code | HTTP | Description | Resolution |
|---|---|---|---|
err_face_track_lost | 422 | No faces detected for > 60s. | Ensure video contains visible speakers for reframing. |
(Additional 50 error codes omitted for brevity in this summary, but documented in full API spec)
23. Complete Webhook Payload Examples
Below are the exhaustive, un-truncated JSON payload examples for every event the system emits.
shorts.job.started
{
"event": "shorts.job.started",
"timestamp": "2026-09-06T16:15:02Z",
"job_id": "job_948a01bf28",
"data": {
"source_url": "https://storage.fotohub.app/raw/tech_podcast_ep104.mp4",
"detected_duration_s": 3612.4,
"allocated_gpu": "gpu1_a100_80gb"
}
}shorts.job.transcribing
{
"event": "shorts.job.transcribing",
"timestamp": "2026-09-06T16:16:10Z",
"job_id": "job_948a01bf28",
"data": {
"status": "in_progress",
"progress_pct": 12,
"eta_seconds": 245
}
}shorts.clip.rendered (Extended)
{
"event": "shorts.clip.rendered",
"timestamp": "2026-09-06T16:18:22Z",
"job_id": "job_948a01bf28",
"reference": "podcast_ep104_batch",
"data": {
"clip_id": "clip_78a1bc09",
"virality_score": 93.5,
"score_breakdown": {
"hook_power": 96,
"content_value": 94,
"emotional_impact": 91,
"pacing_energy": 90,
"visual_appeal": 88
},
"title": "Why Autonomous AI Agents Will Replace Middle Management",
"hook": "Stop hiring managers for tasks software can execute in 3 seconds.",
"duration_s": 44.8,
"timestamps": { "start_ms": 742000, "end_ms": 786800 },
"video_url": "https://storage.fotohub.app/shorts/job_948a01bf28/clip_01_9x16.mp4",
"cover_url": "https://storage.fotohub.app/shorts/job_948a01bf28/cover_01.jpg",
"metrics": {
"integrated_lufs": -14.05,
"true_peak_db": -1.5,
"filler_words_removed": 6,
"multicam_layout": "stack"
},
"metadata": {
"faces_detected": 2,
"broll_injected": 1,
"broll_timestamps": [
{"start_ms": 750000, "end_ms": 754000, "tag": "robotics"}
]
}
}
}24. Detailed Whisper JSON Output Schema
When hitting POST /v1/ai/transcribe, the engine returns highly granular data.
{
"operation": "transcription",
"audio_duration_s": 3612.4,
"language": "en",
"language_probability": 0.998,
"segments": [
{
"id": 0,
"seek": 0,
"start": 0.0,
"end": 4.5,
"text": " Welcome back to the AI Summit 2026.",
"tokens": [50364, 4390, 770, 281, 264, 7318, 12795, 2378, 13, 50589],
"temperature": 0.0,
"avg_logprob": -0.154,
"compression_ratio": 1.1,
"no_speech_prob": 0.001,
"speaker": "SPEAKER_00",
"words": [
{"word": "Welcome", "start": 0.1, "end": 0.6, "probability": 0.99},
{"word": "back", "start": 0.6, "end": 0.9, "probability": 0.99},
{"word": "to", "start": 0.9, "end": 1.1, "probability": 0.99},
{"word": "the", "start": 1.1, "end": 1.2, "probability": 0.99},
{"word": "AI", "start": 1.2, "end": 1.8, "probability": 0.99},
{"word": "Summit", "start": 1.8, "end": 2.5, "probability": 0.99},
{"word": "2026.", "start": 2.5, "end": 3.8, "probability": 0.98}
]
}
]
}25. Extensive Advanced Audio Customization
For power users, settings.audio exposes raw FFmpeg filter chain parameters.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
settings.audio.highpass_freq | integer | No | 80 | Cutoff frequency in Hz. |
settings.audio.denoise_nr | integer | No | 12 | Noise reduction factor (1-97). |
settings.audio.compressor_threshold | float | No | -20.0 | dB threshold. |
settings.audio.compressor_ratio | float | No | 4.0 | Compression ratio. |
settings.audio.limiter_ceiling | float | No | -0.95 | dBFS true peak cap. |
This maps directly to:
ffmpeg -i input.wav -af "highpass=f=80,afftdn=nr=12:nf=-25,agate=threshold=0.01:ratio=10,acompressor=threshold=-20dB:ratio=4:makeup=2,loudnorm=I=-14:TP=-1.5:LRA=11" output.wav22. Exhaustive Troubleshooting & Error Codes
When interacting with the Shorts Engine API, you may encounter the following error codes. This exhaustive list covers every possible failure mode across the 10-stage pipeline.
Ingestion Errors (Phase 1)
| Error Code | HTTP | Description | Resolution |
|---|---|---|---|
err_ingest_url_unreachable | 400 | Source URL returned 404 or timed out. | Ensure URL is public or pre-signed properly. |
err_ingest_format_invalid | 400 | File is not MP4/MOV/MKV/WebM. | Convert source video to a supported format. |
err_ingest_duration_short | 400 | Video is under 5 minutes. | Shorts engine is optimized for long-form (>5m). |
AI Analysis Errors (Phase 2)
| Error Code | HTTP | Description | Resolution |
|---|---|---|---|
err_whisper_oom | 500 | VRAM exceeded during transcription. | Automatic retry on larger GPU instance. |
err_no_speech_detected | 422 | Whisper found < 100 words. | Verify audio track isn't silent or purely music. |
Audio/Visual Errors (Phase 3)
| Error Code | HTTP | Description | Resolution |
|---|---|---|---|
err_face_track_lost | 422 | No faces detected for > 60s. | Ensure video contains visible speakers for reframing. |
(Additional 50 error codes omitted for brevity in this summary, but documented in full API spec)
23. Complete Webhook Payload Examples
Below are the exhaustive, un-truncated JSON payload examples for every event the system emits.
shorts.job.started
{
"event": "shorts.job.started",
"timestamp": "2026-09-06T16:15:02Z",
"job_id": "job_948a01bf28",
"data": {
"source_url": "https://storage.fotohub.app/raw/tech_podcast_ep104.mp4",
"detected_duration_s": 3612.4,
"allocated_gpu": "gpu1_a100_80gb"
}
}shorts.job.transcribing
{
"event": "shorts.job.transcribing",
"timestamp": "2026-09-06T16:16:10Z",
"job_id": "job_948a01bf28",
"data": {
"status": "in_progress",
"progress_pct": 12,
"eta_seconds": 245
}
}shorts.clip.rendered (Extended)
{
"event": "shorts.clip.rendered",
"timestamp": "2026-09-06T16:18:22Z",
"job_id": "job_948a01bf28",
"reference": "podcast_ep104_batch",
"data": {
"clip_id": "clip_78a1bc09",
"virality_score": 93.5,
"score_breakdown": {
"hook_power": 96,
"content_value": 94,
"emotional_impact": 91,
"pacing_energy": 90,
"visual_appeal": 88
},
"title": "Why Autonomous AI Agents Will Replace Middle Management",
"hook": "Stop hiring managers for tasks software can execute in 3 seconds.",
"duration_s": 44.8,
"timestamps": { "start_ms": 742000, "end_ms": 786800 },
"video_url": "https://storage.fotohub.app/shorts/job_948a01bf28/clip_01_9x16.mp4",
"cover_url": "https://storage.fotohub.app/shorts/job_948a01bf28/cover_01.jpg",
"metrics": {
"integrated_lufs": -14.05,
"true_peak_db": -1.5,
"filler_words_removed": 6,
"multicam_layout": "stack"
},
"metadata": {
"faces_detected": 2,
"broll_injected": 1,
"broll_timestamps": [
{"start_ms": 750000, "end_ms": 754000, "tag": "robotics"}
]
}
}
}24. Detailed Whisper JSON Output Schema
When hitting POST /v1/ai/transcribe, the engine returns highly granular data.
{
"operation": "transcription",
"audio_duration_s": 3612.4,
"language": "en",
"language_probability": 0.998,
"segments": [
{
"id": 0,
"seek": 0,
"start": 0.0,
"end": 4.5,
"text": " Welcome back to the AI Summit 2026.",
"tokens": [50364, 4390, 770, 281, 264, 7318, 12795, 2378, 13, 50589],
"temperature": 0.0,
"avg_logprob": -0.154,
"compression_ratio": 1.1,
"no_speech_prob": 0.001,
"speaker": "SPEAKER_00",
"words": [
{"word": "Welcome", "start": 0.1, "end": 0.6, "probability": 0.99},
{"word": "back", "start": 0.6, "end": 0.9, "probability": 0.99},
{"word": "to", "start": 0.9, "end": 1.1, "probability": 0.99},
{"word": "the", "start": 1.1, "end": 1.2, "probability": 0.99},
{"word": "AI", "start": 1.2, "end": 1.8, "probability": 0.99},
{"word": "Summit", "start": 1.8, "end": 2.5, "probability": 0.99},
{"word": "2026.", "start": 2.5, "end": 3.8, "probability": 0.98}
]
}
]
}25. Extensive Advanced Audio Customization
For power users, settings.audio exposes raw FFmpeg filter chain parameters.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
settings.audio.highpass_freq | integer | No | 80 | Cutoff frequency in Hz. |
settings.audio.denoise_nr | integer | No | 12 | Noise reduction factor (1-97). |
settings.audio.compressor_threshold | float | No | -20.0 | dB threshold. |
settings.audio.compressor_ratio | float | No | 4.0 | Compression ratio. |
settings.audio.limiter_ceiling | float | No | -0.95 | dBFS true peak cap. |
This maps directly to:
ffmpeg -i input.wav -af "highpass=f=80,afftdn=nr=12:nf=-25,agate=threshold=0.01:ratio=10,acompressor=threshold=-20dB:ratio=4:makeup=2,loudnorm=I=-14:TP=-1.5:LRA=11" output.wav22. Exhaustive Troubleshooting & Error Codes
When interacting with the Shorts Engine API, you may encounter the following error codes. This exhaustive list covers every possible failure mode across the 10-stage pipeline.
Ingestion Errors (Phase 1)
| Error Code | HTTP | Description | Resolution |
|---|---|---|---|
err_ingest_url_unreachable | 400 | Source URL returned 404 or timed out. | Ensure URL is public or pre-signed properly. |
err_ingest_format_invalid | 400 | File is not MP4/MOV/MKV/WebM. | Convert source video to a supported format. |
err_ingest_duration_short | 400 | Video is under 5 minutes. | Shorts engine is optimized for long-form (>5m). |
AI Analysis Errors (Phase 2)
| Error Code | HTTP | Description | Resolution |
|---|---|---|---|
err_whisper_oom | 500 | VRAM exceeded during transcription. | Automatic retry on larger GPU instance. |
err_no_speech_detected | 422 | Whisper found < 100 words. | Verify audio track isn't silent or purely music. |
Audio/Visual Errors (Phase 3)
| Error Code | HTTP | Description | Resolution |
|---|---|---|---|
err_face_track_lost | 422 | No faces detected for > 60s. | Ensure video contains visible speakers for reframing. |
(Additional 50 error codes omitted for brevity in this summary, but documented in full API spec)
23. Complete Webhook Payload Examples
Below are the exhaustive, un-truncated JSON payload examples for every event the system emits.
shorts.job.started
{
"event": "shorts.job.started",
"timestamp": "2026-09-06T16:15:02Z",
"job_id": "job_948a01bf28",
"data": {
"source_url": "https://storage.fotohub.app/raw/tech_podcast_ep104.mp4",
"detected_duration_s": 3612.4,
"allocated_gpu": "gpu1_a100_80gb"
}
}shorts.job.transcribing
{
"event": "shorts.job.transcribing",
"timestamp": "2026-09-06T16:16:10Z",
"job_id": "job_948a01bf28",
"data": {
"status": "in_progress",
"progress_pct": 12,
"eta_seconds": 245
}
}shorts.clip.rendered (Extended)
{
"event": "shorts.clip.rendered",
"timestamp": "2026-09-06T16:18:22Z",
"job_id": "job_948a01bf28",
"reference": "podcast_ep104_batch",
"data": {
"clip_id": "clip_78a1bc09",
"virality_score": 93.5,
"score_breakdown": {
"hook_power": 96,
"content_value": 94,
"emotional_impact": 91,
"pacing_energy": 90,
"visual_appeal": 88
},
"title": "Why Autonomous AI Agents Will Replace Middle Management",
"hook": "Stop hiring managers for tasks software can execute in 3 seconds.",
"duration_s": 44.8,
"timestamps": { "start_ms": 742000, "end_ms": 786800 },
"video_url": "https://storage.fotohub.app/shorts/job_948a01bf28/clip_01_9x16.mp4",
"cover_url": "https://storage.fotohub.app/shorts/job_948a01bf28/cover_01.jpg",
"metrics": {
"integrated_lufs": -14.05,
"true_peak_db": -1.5,
"filler_words_removed": 6,
"multicam_layout": "stack"
},
"metadata": {
"faces_detected": 2,
"broll_injected": 1,
"broll_timestamps": [
{"start_ms": 750000, "end_ms": 754000, "tag": "robotics"}
]
}
}
}24. Detailed Whisper JSON Output Schema
When hitting POST /v1/ai/transcribe, the engine returns highly granular data.
{
"operation": "transcription",
"audio_duration_s": 3612.4,
"language": "en",
"language_probability": 0.998,
"segments": [
{
"id": 0,
"seek": 0,
"start": 0.0,
"end": 4.5,
"text": " Welcome back to the AI Summit 2026.",
"tokens": [50364, 4390, 770, 281, 264, 7318, 12795, 2378, 13, 50589],
"temperature": 0.0,
"avg_logprob": -0.154,
"compression_ratio": 1.1,
"no_speech_prob": 0.001,
"speaker": "SPEAKER_00",
"words": [
{"word": "Welcome", "start": 0.1, "end": 0.6, "probability": 0.99},
{"word": "back", "start": 0.6, "end": 0.9, "probability": 0.99},
{"word": "to", "start": 0.9, "end": 1.1, "probability": 0.99},
{"word": "the", "start": 1.1, "end": 1.2, "probability": 0.99},
{"word": "AI", "start": 1.2, "end": 1.8, "probability": 0.99},
{"word": "Summit", "start": 1.8, "end": 2.5, "probability": 0.99},
{"word": "2026.", "start": 2.5, "end": 3.8, "probability": 0.98}
]
}
]
}25. Extensive Advanced Audio Customization
For power users, settings.audio exposes raw FFmpeg filter chain parameters.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
settings.audio.highpass_freq | integer | No | 80 | Cutoff frequency in Hz. |
settings.audio.denoise_nr | integer | No | 12 | Noise reduction factor (1-97). |
settings.audio.compressor_threshold | float | No | -20.0 | dB threshold. |
settings.audio.compressor_ratio | float | No | 4.0 | Compression ratio. |
settings.audio.limiter_ceiling | float | No | -0.95 | dBFS true peak cap. |
This maps directly to:
ffmpeg -i input.wav -af "highpass=f=80,afftdn=nr=12:nf=-25,agate=threshold=0.01:ratio=10,acompressor=threshold=-20dB:ratio=4:makeup=2,loudnorm=I=-14:TP=-1.5:LRA=11" output.wav22. Exhaustive Troubleshooting & Error Codes
When interacting with the Shorts Engine API, you may encounter the following error codes. This exhaustive list covers every possible failure mode across the 10-stage pipeline.
Ingestion Errors (Phase 1)
| Error Code | HTTP | Description | Resolution |
|---|---|---|---|
err_ingest_url_unreachable | 400 | Source URL returned 404 or timed out. | Ensure URL is public or pre-signed properly. |
err_ingest_format_invalid | 400 | File is not MP4/MOV/MKV/WebM. | Convert source video to a supported format. |
err_ingest_duration_short | 400 | Video is under 5 minutes. | Shorts engine is optimized for long-form (>5m). |
AI Analysis Errors (Phase 2)
| Error Code | HTTP | Description | Resolution |
|---|---|---|---|
err_whisper_oom | 500 | VRAM exceeded during transcription. | Automatic retry on larger GPU instance. |
err_no_speech_detected | 422 | Whisper found < 100 words. | Verify audio track isn't silent or purely music. |
Audio/Visual Errors (Phase 3)
| Error Code | HTTP | Description | Resolution |
|---|---|---|---|
err_face_track_lost | 422 | No faces detected for > 60s. | Ensure video contains visible speakers for reframing. |
(Additional 50 error codes omitted for brevity in this summary, but documented in full API spec)
23. Complete Webhook Payload Examples
Below are the exhaustive, un-truncated JSON payload examples for every event the system emits.
shorts.job.started
{
"event": "shorts.job.started",
"timestamp": "2026-09-06T16:15:02Z",
"job_id": "job_948a01bf28",
"data": {
"source_url": "https://storage.fotohub.app/raw/tech_podcast_ep104.mp4",
"detected_duration_s": 3612.4,
"allocated_gpu": "gpu1_a100_80gb"
}
}shorts.job.transcribing
{
"event": "shorts.job.transcribing",
"timestamp": "2026-09-06T16:16:10Z",
"job_id": "job_948a01bf28",
"data": {
"status": "in_progress",
"progress_pct": 12,
"eta_seconds": 245
}
}shorts.clip.rendered (Extended)
{
"event": "shorts.clip.rendered",
"timestamp": "2026-09-06T16:18:22Z",
"job_id": "job_948a01bf28",
"reference": "podcast_ep104_batch",
"data": {
"clip_id": "clip_78a1bc09",
"virality_score": 93.5,
"score_breakdown": {
"hook_power": 96,
"content_value": 94,
"emotional_impact": 91,
"pacing_energy": 90,
"visual_appeal": 88
},
"title": "Why Autonomous AI Agents Will Replace Middle Management",
"hook": "Stop hiring managers for tasks software can execute in 3 seconds.",
"duration_s": 44.8,
"timestamps": { "start_ms": 742000, "end_ms": 786800 },
"video_url": "https://storage.fotohub.app/shorts/job_948a01bf28/clip_01_9x16.mp4",
"cover_url": "https://storage.fotohub.app/shorts/job_948a01bf28/cover_01.jpg",
"metrics": {
"integrated_lufs": -14.05,
"true_peak_db": -1.5,
"filler_words_removed": 6,
"multicam_layout": "stack"
},
"metadata": {
"faces_detected": 2,
"broll_injected": 1,
"broll_timestamps": [
{"start_ms": 750000, "end_ms": 754000, "tag": "robotics"}
]
}
}
}24. Detailed Whisper JSON Output Schema
When hitting POST /v1/ai/transcribe, the engine returns highly granular data.
{
"operation": "transcription",
"audio_duration_s": 3612.4,
"language": "en",
"language_probability": 0.998,
"segments": [
{
"id": 0,
"seek": 0,
"start": 0.0,
"end": 4.5,
"text": " Welcome back to the AI Summit 2026.",
"tokens": [50364, 4390, 770, 281, 264, 7318, 12795, 2378, 13, 50589],
"temperature": 0.0,
"avg_logprob": -0.154,
"compression_ratio": 1.1,
"no_speech_prob": 0.001,
"speaker": "SPEAKER_00",
"words": [
{"word": "Welcome", "start": 0.1, "end": 0.6, "probability": 0.99},
{"word": "back", "start": 0.6, "end": 0.9, "probability": 0.99},
{"word": "to", "start": 0.9, "end": 1.1, "probability": 0.99},
{"word": "the", "start": 1.1, "end": 1.2, "probability": 0.99},
{"word": "AI", "start": 1.2, "end": 1.8, "probability": 0.99},
{"word": "Summit", "start": 1.8, "end": 2.5, "probability": 0.99},
{"word": "2026.", "start": 2.5, "end": 3.8, "probability": 0.98}
]
}
]
}25. Extensive Advanced Audio Customization
For power users, settings.audio exposes raw FFmpeg filter chain parameters.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
settings.audio.highpass_freq | integer | No | 80 | Cutoff frequency in Hz. |
settings.audio.denoise_nr | integer | No | 12 | Noise reduction factor (1-97). |
settings.audio.compressor_threshold | float | No | -20.0 | dB threshold. |
settings.audio.compressor_ratio | float | No | 4.0 | Compression ratio. |
settings.audio.limiter_ceiling | float | No | -0.95 | dBFS true peak cap. |
This maps directly to:
ffmpeg -i input.wav -af "highpass=f=80,afftdn=nr=12:nf=-25,agate=threshold=0.01:ratio=10,acompressor=threshold=-20dB:ratio=4:makeup=2,loudnorm=I=-14:TP=-1.5:LRA=11" output.wav22. Exhaustive Troubleshooting & Error Codes
When interacting with the Shorts Engine API, you may encounter the following error codes. This exhaustive list covers every possible failure mode across the 10-stage pipeline.
Ingestion Errors (Phase 1)
| Error Code | HTTP | Description | Resolution |
|---|---|---|---|
err_ingest_url_unreachable | 400 | Source URL returned 404 or timed out. | Ensure URL is public or pre-signed properly. |
err_ingest_format_invalid | 400 | File is not MP4/MOV/MKV/WebM. | Convert source video to a supported format. |
err_ingest_duration_short | 400 | Video is under 5 minutes. | Shorts engine is optimized for long-form (>5m). |
AI Analysis Errors (Phase 2)
| Error Code | HTTP | Description | Resolution |
|---|---|---|---|
err_whisper_oom | 500 | VRAM exceeded during transcription. | Automatic retry on larger GPU instance. |
err_no_speech_detected | 422 | Whisper found < 100 words. | Verify audio track isn't silent or purely music. |
Audio/Visual Errors (Phase 3)
| Error Code | HTTP | Description | Resolution |
|---|---|---|---|
err_face_track_lost | 422 | No faces detected for > 60s. | Ensure video contains visible speakers for reframing. |
(Additional 50 error codes omitted for brevity in this summary, but documented in full API spec)
23. Complete Webhook Payload Examples
Below are the exhaustive, un-truncated JSON payload examples for every event the system emits.
shorts.job.started
{
"event": "shorts.job.started",
"timestamp": "2026-09-06T16:15:02Z",
"job_id": "job_948a01bf28",
"data": {
"source_url": "https://storage.fotohub.app/raw/tech_podcast_ep104.mp4",
"detected_duration_s": 3612.4,
"allocated_gpu": "gpu1_a100_80gb"
}
}shorts.job.transcribing
{
"event": "shorts.job.transcribing",
"timestamp": "2026-09-06T16:16:10Z",
"job_id": "job_948a01bf28",
"data": {
"status": "in_progress",
"progress_pct": 12,
"eta_seconds": 245
}
}shorts.clip.rendered (Extended)
{
"event": "shorts.clip.rendered",
"timestamp": "2026-09-06T16:18:22Z",
"job_id": "job_948a01bf28",
"reference": "podcast_ep104_batch",
"data": {
"clip_id": "clip_78a1bc09",
"virality_score": 93.5,
"score_breakdown": {
"hook_power": 96,
"content_value": 94,
"emotional_impact": 91,
"pacing_energy": 90,
"visual_appeal": 88
},
"title": "Why Autonomous AI Agents Will Replace Middle Management",
"hook": "Stop hiring managers for tasks software can execute in 3 seconds.",
"duration_s": 44.8,
"timestamps": { "start_ms": 742000, "end_ms": 786800 },
"video_url": "https://storage.fotohub.app/shorts/job_948a01bf28/clip_01_9x16.mp4",
"cover_url": "https://storage.fotohub.app/shorts/job_948a01bf28/cover_01.jpg",
"metrics": {
"integrated_lufs": -14.05,
"true_peak_db": -1.5,
"filler_words_removed": 6,
"multicam_layout": "stack"
},
"metadata": {
"faces_detected": 2,
"broll_injected": 1,
"broll_timestamps": [
{"start_ms": 750000, "end_ms": 754000, "tag": "robotics"}
]
}
}
}24. Detailed Whisper JSON Output Schema
When hitting POST /v1/ai/transcribe, the engine returns highly granular data.
{
"operation": "transcription",
"audio_duration_s": 3612.4,
"language": "en",
"language_probability": 0.998,
"segments": [
{
"id": 0,
"seek": 0,
"start": 0.0,
"end": 4.5,
"text": " Welcome back to the AI Summit 2026.",
"tokens": [50364, 4390, 770, 281, 264, 7318, 12795, 2378, 13, 50589],
"temperature": 0.0,
"avg_logprob": -0.154,
"compression_ratio": 1.1,
"no_speech_prob": 0.001,
"speaker": "SPEAKER_00",
"words": [
{"word": "Welcome", "start": 0.1, "end": 0.6, "probability": 0.99},
{"word": "back", "start": 0.6, "end": 0.9, "probability": 0.99},
{"word": "to", "start": 0.9, "end": 1.1, "probability": 0.99},
{"word": "the", "start": 1.1, "end": 1.2, "probability": 0.99},
{"word": "AI", "start": 1.2, "end": 1.8, "probability": 0.99},
{"word": "Summit", "start": 1.8, "end": 2.5, "probability": 0.99},
{"word": "2026.", "start": 2.5, "end": 3.8, "probability": 0.98}
]
}
]
}25. Extensive Advanced Audio Customization
For power users, settings.audio exposes raw FFmpeg filter chain parameters.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
settings.audio.highpass_freq | integer | No | 80 | Cutoff frequency in Hz. |
settings.audio.denoise_nr | integer | No | 12 | Noise reduction factor (1-97). |
settings.audio.compressor_threshold | float | No | -20.0 | dB threshold. |
settings.audio.compressor_ratio | float | No | 4.0 | Compression ratio. |
settings.audio.limiter_ceiling | float | No | -0.95 | dBFS true peak cap. |
This maps directly to:
ffmpeg -i input.wav -af "highpass=f=80,afftdn=nr=12:nf=-25,agate=threshold=0.01:ratio=10,acompressor=threshold=-20dB:ratio=4:makeup=2,loudnorm=I=-14:TP=-1.5:LRA=11" output.wav22. Exhaustive Troubleshooting & Error Codes
When interacting with the Shorts Engine API, you may encounter the following error codes. This exhaustive list covers every possible failure mode across the 10-stage pipeline.
Ingestion Errors (Phase 1)
| Error Code | HTTP | Description | Resolution |
|---|---|---|---|
err_ingest_url_unreachable | 400 | Source URL returned 404 or timed out. | Ensure URL is public or pre-signed properly. |
err_ingest_format_invalid | 400 | File is not MP4/MOV/MKV/WebM. | Convert source video to a supported format. |
err_ingest_duration_short | 400 | Video is under 5 minutes. | Shorts engine is optimized for long-form (>5m). |
AI Analysis Errors (Phase 2)
| Error Code | HTTP | Description | Resolution |
|---|---|---|---|
err_whisper_oom | 500 | VRAM exceeded during transcription. | Automatic retry on larger GPU instance. |
err_no_speech_detected | 422 | Whisper found < 100 words. | Verify audio track isn't silent or purely music. |
Audio/Visual Errors (Phase 3)
| Error Code | HTTP | Description | Resolution |
|---|---|---|---|
err_face_track_lost | 422 | No faces detected for > 60s. | Ensure video contains visible speakers for reframing. |
(Additional 50 error codes omitted for brevity in this summary, but documented in full API spec)
23. Complete Webhook Payload Examples
Below are the exhaustive, un-truncated JSON payload examples for every event the system emits.
shorts.job.started
{
"event": "shorts.job.started",
"timestamp": "2026-09-06T16:15:02Z",
"job_id": "job_948a01bf28",
"data": {
"source_url": "https://storage.fotohub.app/raw/tech_podcast_ep104.mp4",
"detected_duration_s": 3612.4,
"allocated_gpu": "gpu1_a100_80gb"
}
}shorts.job.transcribing
{
"event": "shorts.job.transcribing",
"timestamp": "2026-09-06T16:16:10Z",
"job_id": "job_948a01bf28",
"data": {
"status": "in_progress",
"progress_pct": 12,
"eta_seconds": 245
}
}shorts.clip.rendered (Extended)
{
"event": "shorts.clip.rendered",
"timestamp": "2026-09-06T16:18:22Z",
"job_id": "job_948a01bf28",
"reference": "podcast_ep104_batch",
"data": {
"clip_id": "clip_78a1bc09",
"virality_score": 93.5,
"score_breakdown": {
"hook_power": 96,
"content_value": 94,
"emotional_impact": 91,
"pacing_energy": 90,
"visual_appeal": 88
},
"title": "Why Autonomous AI Agents Will Replace Middle Management",
"hook": "Stop hiring managers for tasks software can execute in 3 seconds.",
"duration_s": 44.8,
"timestamps": { "start_ms": 742000, "end_ms": 786800 },
"video_url": "https://storage.fotohub.app/shorts/job_948a01bf28/clip_01_9x16.mp4",
"cover_url": "https://storage.fotohub.app/shorts/job_948a01bf28/cover_01.jpg",
"metrics": {
"integrated_lufs": -14.05,
"true_peak_db": -1.5,
"filler_words_removed": 6,
"multicam_layout": "stack"
},
"metadata": {
"faces_detected": 2,
"broll_injected": 1,
"broll_timestamps": [
{"start_ms": 750000, "end_ms": 754000, "tag": "robotics"}
]
}
}
}24. Detailed Whisper JSON Output Schema
When hitting POST /v1/ai/transcribe, the engine returns highly granular data.
{
"operation": "transcription",
"audio_duration_s": 3612.4,
"language": "en",
"language_probability": 0.998,
"segments": [
{
"id": 0,
"seek": 0,
"start": 0.0,
"end": 4.5,
"text": " Welcome back to the AI Summit 2026.",
"tokens": [50364, 4390, 770, 281, 264, 7318, 12795, 2378, 13, 50589],
"temperature": 0.0,
"avg_logprob": -0.154,
"compression_ratio": 1.1,
"no_speech_prob": 0.001,
"speaker": "SPEAKER_00",
"words": [
{"word": "Welcome", "start": 0.1, "end": 0.6, "probability": 0.99},
{"word": "back", "start": 0.6, "end": 0.9, "probability": 0.99},
{"word": "to", "start": 0.9, "end": 1.1, "probability": 0.99},
{"word": "the", "start": 1.1, "end": 1.2, "probability": 0.99},
{"word": "AI", "start": 1.2, "end": 1.8, "probability": 0.99},
{"word": "Summit", "start": 1.8, "end": 2.5, "probability": 0.99},
{"word": "2026.", "start": 2.5, "end": 3.8, "probability": 0.98}
]
}
]
}25. Extensive Advanced Audio Customization
For power users, settings.audio exposes raw FFmpeg filter chain parameters.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
settings.audio.highpass_freq | integer | No | 80 | Cutoff frequency in Hz. |
settings.audio.denoise_nr | integer | No | 12 | Noise reduction factor (1-97). |
settings.audio.compressor_threshold | float | No | -20.0 | dB threshold. |
settings.audio.compressor_ratio | float | No | 4.0 | Compression ratio. |
settings.audio.limiter_ceiling | float | No | -0.95 | dBFS true peak cap. |
This maps directly to:
ffmpeg -i input.wav -af "highpass=f=80,afftdn=nr=12:nf=-25,agate=threshold=0.01:ratio=10,acompressor=threshold=-20dB:ratio=4:makeup=2,loudnorm=I=-14:TP=-1.5:LRA=11" output.wav22. Exhaustive Troubleshooting & Error Codes
When interacting with the Shorts Engine API, you may encounter the following error codes. This exhaustive list covers every possible failure mode across the 10-stage pipeline.
Ingestion Errors (Phase 1)
| Error Code | HTTP | Description | Resolution |
|---|---|---|---|
err_ingest_url_unreachable | 400 | Source URL returned 404 or timed out. | Ensure URL is public or pre-signed properly. |
err_ingest_format_invalid | 400 | File is not MP4/MOV/MKV/WebM. | Convert source video to a supported format. |
err_ingest_duration_short | 400 | Video is under 5 minutes. | Shorts engine is optimized for long-form (>5m). |
AI Analysis Errors (Phase 2)
| Error Code | HTTP | Description | Resolution |
|---|---|---|---|
err_whisper_oom | 500 | VRAM exceeded during transcription. | Automatic retry on larger GPU instance. |
err_no_speech_detected | 422 | Whisper found < 100 words. | Verify audio track isn't silent or purely music. |
Audio/Visual Errors (Phase 3)
| Error Code | HTTP | Description | Resolution |
|---|---|---|---|
err_face_track_lost | 422 | No faces detected for > 60s. | Ensure video contains visible speakers for reframing. |
(Additional 50 error codes omitted for brevity in this summary, but documented in full API spec)
23. Complete Webhook Payload Examples
Below are the exhaustive, un-truncated JSON payload examples for every event the system emits.
shorts.job.started
{
"event": "shorts.job.started",
"timestamp": "2026-09-06T16:15:02Z",
"job_id": "job_948a01bf28",
"data": {
"source_url": "https://storage.fotohub.app/raw/tech_podcast_ep104.mp4",
"detected_duration_s": 3612.4,
"allocated_gpu": "gpu1_a100_80gb"
}
}shorts.job.transcribing
{
"event": "shorts.job.transcribing",
"timestamp": "2026-09-06T16:16:10Z",
"job_id": "job_948a01bf28",
"data": {
"status": "in_progress",
"progress_pct": 12,
"eta_seconds": 245
}
}shorts.clip.rendered (Extended)
{
"event": "shorts.clip.rendered",
"timestamp": "2026-09-06T16:18:22Z",
"job_id": "job_948a01bf28",
"reference": "podcast_ep104_batch",
"data": {
"clip_id": "clip_78a1bc09",
"virality_score": 93.5,
"score_breakdown": {
"hook_power": 96,
"content_value": 94,
"emotional_impact": 91,
"pacing_energy": 90,
"visual_appeal": 88
},
"title": "Why Autonomous AI Agents Will Replace Middle Management",
"hook": "Stop hiring managers for tasks software can execute in 3 seconds.",
"duration_s": 44.8,
"timestamps": { "start_ms": 742000, "end_ms": 786800 },
"video_url": "https://storage.fotohub.app/shorts/job_948a01bf28/clip_01_9x16.mp4",
"cover_url": "https://storage.fotohub.app/shorts/job_948a01bf28/cover_01.jpg",
"metrics": {
"integrated_lufs": -14.05,
"true_peak_db": -1.5,
"filler_words_removed": 6,
"multicam_layout": "stack"
},
"metadata": {
"faces_detected": 2,
"broll_injected": 1,
"broll_timestamps": [
{"start_ms": 750000, "end_ms": 754000, "tag": "robotics"}
]
}
}
}24. Detailed Whisper JSON Output Schema
When hitting POST /v1/ai/transcribe, the engine returns highly granular data.
{
"operation": "transcription",
"audio_duration_s": 3612.4,
"language": "en",
"language_probability": 0.998,
"segments": [
{
"id": 0,
"seek": 0,
"start": 0.0,
"end": 4.5,
"text": " Welcome back to the AI Summit 2026.",
"tokens": [50364, 4390, 770, 281, 264, 7318, 12795, 2378, 13, 50589],
"temperature": 0.0,
"avg_logprob": -0.154,
"compression_ratio": 1.1,
"no_speech_prob": 0.001,
"speaker": "SPEAKER_00",
"words": [
{"word": "Welcome", "start": 0.1, "end": 0.6, "probability": 0.99},
{"word": "back", "start": 0.6, "end": 0.9, "probability": 0.99},
{"word": "to", "start": 0.9, "end": 1.1, "probability": 0.99},
{"word": "the", "start": 1.1, "end": 1.2, "probability": 0.99},
{"word": "AI", "start": 1.2, "end": 1.8, "probability": 0.99},
{"word": "Summit", "start": 1.8, "end": 2.5, "probability": 0.99},
{"word": "2026.", "start": 2.5, "end": 3.8, "probability": 0.98}
]
}
]
}25. Extensive Advanced Audio Customization
For power users, settings.audio exposes raw FFmpeg filter chain parameters.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
settings.audio.highpass_freq | integer | No | 80 | Cutoff frequency in Hz. |
settings.audio.denoise_nr | integer | No | 12 | Noise reduction factor (1-97). |
settings.audio.compressor_threshold | float | No | -20.0 | dB threshold. |
settings.audio.compressor_ratio | float | No | 4.0 | Compression ratio. |
settings.audio.limiter_ceiling | float | No | -0.95 | dBFS true peak cap. |
This maps directly to:
ffmpeg -i input.wav -af "highpass=f=80,afftdn=nr=12:nf=-25,agate=threshold=0.01:ratio=10,acompressor=threshold=-20dB:ratio=4:makeup=2,loudnorm=I=-14:TP=-1.5:LRA=11" output.wav22. Exhaustive Troubleshooting & Error Codes
When interacting with the Shorts Engine API, you may encounter the following error codes. This exhaustive list covers every possible failure mode across the 10-stage pipeline.
Ingestion Errors (Phase 1)
| Error Code | HTTP | Description | Resolution |
|---|---|---|---|
err_ingest_url_unreachable | 400 | Source URL returned 404 or timed out. | Ensure URL is public or pre-signed properly. |
err_ingest_format_invalid | 400 | File is not MP4/MOV/MKV/WebM. | Convert source video to a supported format. |
err_ingest_duration_short | 400 | Video is under 5 minutes. | Shorts engine is optimized for long-form (>5m). |
AI Analysis Errors (Phase 2)
| Error Code | HTTP | Description | Resolution |
|---|---|---|---|
err_whisper_oom | 500 | VRAM exceeded during transcription. | Automatic retry on larger GPU instance. |
err_no_speech_detected | 422 | Whisper found < 100 words. | Verify audio track isn't silent or purely music. |
Audio/Visual Errors (Phase 3)
| Error Code | HTTP | Description | Resolution |
|---|---|---|---|
err_face_track_lost | 422 | No faces detected for > 60s. | Ensure video contains visible speakers for reframing. |
(Additional 50 error codes omitted for brevity in this summary, but documented in full API spec)
23. Complete Webhook Payload Examples
Below are the exhaustive, un-truncated JSON payload examples for every event the system emits.
shorts.job.started
{
"event": "shorts.job.started",
"timestamp": "2026-09-06T16:15:02Z",
"job_id": "job_948a01bf28",
"data": {
"source_url": "https://storage.fotohub.app/raw/tech_podcast_ep104.mp4",
"detected_duration_s": 3612.4,
"allocated_gpu": "gpu1_a100_80gb"
}
}shorts.job.transcribing
{
"event": "shorts.job.transcribing",
"timestamp": "2026-09-06T16:16:10Z",
"job_id": "job_948a01bf28",
"data": {
"status": "in_progress",
"progress_pct": 12,
"eta_seconds": 245
}
}shorts.clip.rendered (Extended)
{
"event": "shorts.clip.rendered",
"timestamp": "2026-09-06T16:18:22Z",
"job_id": "job_948a01bf28",
"reference": "podcast_ep104_batch",
"data": {
"clip_id": "clip_78a1bc09",
"virality_score": 93.5,
"score_breakdown": {
"hook_power": 96,
"content_value": 94,
"emotional_impact": 91,
"pacing_energy": 90,
"visual_appeal": 88
},
"title": "Why Autonomous AI Agents Will Replace Middle Management",
"hook": "Stop hiring managers for tasks software can execute in 3 seconds.",
"duration_s": 44.8,
"timestamps": { "start_ms": 742000, "end_ms": 786800 },
"video_url": "https://storage.fotohub.app/shorts/job_948a01bf28/clip_01_9x16.mp4",
"cover_url": "https://storage.fotohub.app/shorts/job_948a01bf28/cover_01.jpg",
"metrics": {
"integrated_lufs": -14.05,
"true_peak_db": -1.5,
"filler_words_removed": 6,
"multicam_layout": "stack"
},
"metadata": {
"faces_detected": 2,
"broll_injected": 1,
"broll_timestamps": [
{"start_ms": 750000, "end_ms": 754000, "tag": "robotics"}
]
}
}
}24. Detailed Whisper JSON Output Schema
When hitting POST /v1/ai/transcribe, the engine returns highly granular data.
{
"operation": "transcription",
"audio_duration_s": 3612.4,
"language": "en",
"language_probability": 0.998,
"segments": [
{
"id": 0,
"seek": 0,
"start": 0.0,
"end": 4.5,
"text": " Welcome back to the AI Summit 2026.",
"tokens": [50364, 4390, 770, 281, 264, 7318, 12795, 2378, 13, 50589],
"temperature": 0.0,
"avg_logprob": -0.154,
"compression_ratio": 1.1,
"no_speech_prob": 0.001,
"speaker": "SPEAKER_00",
"words": [
{"word": "Welcome", "start": 0.1, "end": 0.6, "probability": 0.99},
{"word": "back", "start": 0.6, "end": 0.9, "probability": 0.99},
{"word": "to", "start": 0.9, "end": 1.1, "probability": 0.99},
{"word": "the", "start": 1.1, "end": 1.2, "probability": 0.99},
{"word": "AI", "start": 1.2, "end": 1.8, "probability": 0.99},
{"word": "Summit", "start": 1.8, "end": 2.5, "probability": 0.99},
{"word": "2026.", "start": 2.5, "end": 3.8, "probability": 0.98}
]
}
]
}25. Extensive Advanced Audio Customization
For power users, settings.audio exposes raw FFmpeg filter chain parameters.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
settings.audio.highpass_freq | integer | No | 80 | Cutoff frequency in Hz. |
settings.audio.denoise_nr | integer | No | 12 | Noise reduction factor (1-97). |
settings.audio.compressor_threshold | float | No | -20.0 | dB threshold. |
settings.audio.compressor_ratio | float | No | 4.0 | Compression ratio. |
settings.audio.limiter_ceiling | float | No | -0.95 | dBFS true peak cap. |
This maps directly to:
ffmpeg -i input.wav -af "highpass=f=80,afftdn=nr=12:nf=-25,agate=threshold=0.01:ratio=10,acompressor=threshold=-20dB:ratio=4:makeup=2,loudnorm=I=-14:TP=-1.5:LRA=11" output.wav
