ViddyFlow Documentation

Getting Started

ViddyFlow is an AI-powered highlight studio that automatically transforms your livestream VODs into engaging highlight videos and social-ready clips.

1. Create Your Account

Sign up at app.viddyflow.com/register and get 500 free credits to start.

2. Submit Your Video

Paste a public Twitch VOD URL, or upload an MP4 or MOV file directly. Uploaded files are scored on video and audio. Twitch chat analysis applies to Twitch VODs only.

3. Choose Processing Mode

Select from 6 processing tiers ranging from Rapid Sprint (18 credits/hour) to Director Finish (90 credits/hour).

4. Get Your Highlights

Receive AI-generated highlight videos and vertical shorts optimized for social platforms.

API Documentation

ViddyFlow provides a RESTful API for programmatic access to all features. Base URL: https://api.viddyflow.com

Authentication

Generate your API key in the API Access dashboard and include it in the request header:

X-API-Key: your_api_key_here

Base URL: https://api.viddyflow.com. The fastest way to make your first call is curl:

curl -X POST https://api.viddyflow.com/jobs \
  -H "X-API-Key: your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"vod_url": "https://www.twitch.tv/videos/123456789", "num_shorts": 3}'

Note: Requests authenticated with an API key receive a trimmed, public-facing response (a lightweight acknowledgement on POST /jobs, and sanitized job/results objects), whereas the dashboard's logged-in session sees the full internal object. The fields documented below are what API-key callers receive.

Quick Start

Follow this checklist to mirror the dashboard experience via API. The request schema matches the "New Job" form and the service measures the VOD duration automatically as soon as a job is queued.

  1. Generate a key and store it securely: each value is displayed once.
  2. Add the key to every call with the X-API-Key header.
  3. POST to /jobs with your VOD URL and desired settings. Duration is calculated automatically.
  4. The response is a lightweight acknowledgement that echoes the job id and credit charge. Use that id to poll /jobs/:id.
  5. Provide run presets, highlight bounds, streamer metadata, and optional webhook URLs to align with your dashboard defaults.
  6. Check status via GET /jobs/:id or rely on your webhook for completion events.

Request Fields

vod_url

The video source: a public Twitch VOD URL (e.g. https://www.twitch.tv/videos/123456789). For non-Twitch sources, upload the file from the dashboard instead.

streamer_name, streamer_pronouns, game_name

Improve metadata on the final cut.

streamer_twitch_url / streamer_twitter_url

Optional social links for title/description generation.

run_profiles

Choose any combination of balanced, audience, or streamer presets.

run_overrides

Override the weights, clip cohesion, and target length per preset (see examples below).

compilation_enabled

Set to false for a shorts-only job: no highlight reel is rendered, and no generation credits are charged for one; you pay only processing plus your shorts. Requires num_shorts greater than 0, since the job must still produce something. Defaults to true, so omitting it keeps the normal reel + shorts behaviour.

Note that run_profiles is still required and still matters: the shorts are selected from the moments those presets score, so they decide which clips you get. Turning this off drops the reel deliverable, not the analysis behind it.

final_video_length_minutes

Target reel runtime used for the credit estimate. Omit it to have the server derive it from your presets and run_overrides. Must be greater than 0 unless compilation_enabled is false, in which case the server forces it to 0 for you.

parameters

Supply min_highlight_duration / max_highlight_duration just like the dashboard sliders.

Teaser intro (cold open): a short montage of the reel's best moments prepended to the highlight reel. Enabled by default; control it with these keys inside parameters:

  • cold_open_enabled: true (default) or false to skip the teaser.
  • cold_open_max_clips: snippets in the teaser, clamped to 2-6 (default 4).
  • cold_open_snippet_duration: seconds per snippet, clamped to 2.0-10.0 (default 5).

Title card uses reel_title_card_enabled: true flashes each clip's story name for a moment at the start of the reel, and also shows it on each teaser snippet when the teaser is enabled. Defaults to false (a user choice, not a forced overlay).

Background music is a dashboard feature.

webhook_url

Subscribe to job status callbacks.

stream_language

ISO 639-1 code for the streamer's spoken language (e.g. de, ja, ar). Overrides automatic language detection. Leave blank to detect automatically.

metadata_language

ISO 639-1 code for the generated title, description, chapters, and tags. Defaults to stream_language when omitted. Set to en to always get English metadata. Supported: ar zh cs da nl fi fr de el hi hu id it ja ko no fa pl pt ro ru es sv tl th tr uk vi.

num_shorts

Number of vertical 9:16 short clips to generate alongside the highlight reel (0 to skip). Charged at 10 credits per short, separate from the reel cost. Tier caps: free trial = 3, pay-as-you-go = 5, Pro/Studio = 10. The server silently clamps values above your tier cap. Omit or set to 0 to skip shorts entirely.

burn_captions

When true, captions are burned directly into each vertical short: white text with black outline, synced from the stream transcript. Defaults to true when num_shorts > 0. Set to false to receive clean clips without captions. Has no effect when num_shorts is 0.

shorts_crop_mode (nested under parameters)

How a 16:9 source frame becomes 9:16 vertical. "crop" (default) center-crops to fill the frame. On a 1080p source that keeps roughly a third of the frame width, which can cut off a facecam, minimap, or killfeed in a corner. "fit" scales the whole frame in instead, padding top and bottom with black (nothing cropped, at the cost of the footage occupying only the middle band). "split" crops one region onto the top half and another onto the bottom half, stacked to fill the whole frame with no blank space; see shorts_facecam_*/shorts_gameplay_* below. Any other value falls back to "crop".

shorts_facecam_x, shorts_facecam_y, shorts_facecam_width, shorts_facecam_height (nested under parameters, only used when shorts_crop_mode is "split")

The top region's position and size as fractions (0.0-1.0) of the full source frame (typically a facecam), e.g. a facecam in the bottom-right quarter of the screen is roughly x=0.68, y=0.62, width=0.3, height=0.36. All four must be present and numeric, or a default bottom-right box is used instead. If the box runs past the frame edge, the position is kept and the size shrinks to fit from there.

shorts_gameplay_x, shorts_gameplay_y, shorts_gameplay_width, shorts_gameplay_height (nested under parameters, only used when shorts_crop_mode is "split")

Same format as shorts_facecam_*, but for the bottom region. Defaults to the whole frame (x=0, y=0, width=1, height=1) when omitted. Set explicitly to exclude chat or overlay panels from the bottom half too.

shorts_split_ratio (nested under parameters, only used when shorts_crop_mode is "split")

The fraction (0.0-1.0) of the vertical canvas the top (facecam) region gets; the bottom (gameplay) region gets the rest. Defaults to 0.5, an even 50/50 stack. Out-of-range values are clamped to 0.15-0.85 rather than rejected. Useful when the facecam region is naturally narrow or tall: a bigger ratio gives it more vertical room instead of forcing a fixed-shape crop.

shorts_facecam_frame_enabled (nested under parameters, only used when shorts_crop_mode is "split")

Set to true to draw a colored outline around the facecam region only, separating it visually from the gameplay region below. Defaults to false.

shorts_facecam_frame_color (nested under parameters, only used when shorts_facecam_frame_enabled is true)

One of "white", "black", "violet". Defaults to "white"; an unrecognized value falls back to it.

shorts_title_card_enabled (nested under parameters)

Set to true to flash the LLM-generated clip title for a moment at the start of each short. Defaults to false (a user choice, not a forced overlay).

Auto-generated captions are also saved as a downloadable .srt file alongside each short, whether or not burn_captions burned them into the video, so you can fix a line without starting from scratch. No parameter needed, it's included automatically.

Tip: Duration fields are automatically calculated based on your VOD. Just provide the URL and your preferred settings!

Create Processing Job

POST /jobs

Submit a VOD for AI processing to generate highlights and clips.

Full Request Example:

{
  "vod_url": "https://twitch.tv/videos/123456789",
  "streamer_name": "StreamerName",
  "game_name": "Game Name",
  "streamer_pronouns": "they/them",
  "streamer_twitch_url": "https://twitch.tv/StreamerName",
  "webhook_url": "https://hooks.example.com/viddyflow/status",
  "stream_language": "en",
  "metadata_language": "en",
  "run_profiles": ["audience"],
  "model_preset": "rapid_sprint",
  "num_shorts": 5,
  "burn_captions": true,
  "parameters": {
    "min_highlight_duration": 30,
    "max_highlight_duration": 60,
    "cold_open_enabled": true,
    "cold_open_max_clips": 4,
    "cold_open_snippet_duration": 5
  }
}

Model Presets:

  • rapid_sprint - 18 credits/hour (fastest)
  • stable_tempo - 25 credits/hour
  • creator_focus - 38 credits/hour (recommended)
  • studio_polish - 55 credits/hour
  • auto_balance - 65 credits/hour
  • director_finish - 90 credits/hour (maximum quality)

Run Profiles:

balanced - Blend hype with creator moments
  • • audience_weight: 0.5, streamer_weight: 0.5
  • • similarity_threshold: 0.7 (clip cohesion)
  • • target_duration_minutes: 8
audience - Chat spikes & viral reactions
  • • audience_weight: 0.8, streamer_weight: 0.2
  • • similarity_threshold: 0.7
  • • target_duration_minutes: 5
streamer - Playmaking & commentary
  • • audience_weight: 0.2, streamer_weight: 0.8
  • • similarity_threshold: 0.7
  • • target_duration_minutes: 10

Python Example:

import requests

API_BASE = "https://api.viddyflow.com"
API_KEY = "your_api_key_here"

payload = {
    "vod_url": "https://twitch.tv/videos/123456789",
    "streamer_name": "StreamerName",
    "game_name": "Game Name",
    "stream_language": "en",   # optional: override audio language detection
    "metadata_language": "en", # optional: language for title/description/chapters
    "run_profiles": ["audience"],
    "model_preset": "rapid_sprint",
    "num_shorts": 5,
    "burn_captions": True,
    "parameters": {
        "min_highlight_duration": 30,
        "max_highlight_duration": 60
    }
}

response = requests.post(
    f"{API_BASE}/jobs",
    headers={"X-API-Key": API_KEY},
    json=payload,
    timeout=30,
)
response.raise_for_status()
data = response.json()
print(f"Job ID: {data['id']}")
print(f"Credit cost: {data['credit_cost']}")

Node.js Example:

const API_BASE = "https://api.viddyflow.com";
const API_KEY = "your_api_key_here";

async function submitJob() {
  const payload = {
    vod_url: "https://twitch.tv/videos/123456789",
    streamer_name: "StreamerName",
    game_name: "Game Name",
    stream_language: "en",   // optional: override audio language detection
    metadata_language: "en", // optional: language for title/description/chapters
    run_profiles: ["audience"],
    model_preset: "rapid_sprint",
    num_shorts: 5,
    burn_captions: true,
    parameters: {
      min_highlight_duration: 30,
      max_highlight_duration: 60
    }
  };

  const response = await fetch(`${API_BASE}/jobs`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-API-Key": API_KEY,
    },
    body: JSON.stringify(payload),
  });

  if (!response.ok) {
    throw new Error(`Failed: ${response.status}`);
  }

  const data = await response.json();
  console.log("Job ID:", data.id);
  console.log("Credit cost:", data.credit_cost);
}

submitJob().catch(console.error);

Response:

{
  "id": "abc123-def456-ghi789",
  "status": "pending",
  "webhook_url": null,
  "created_at": "2025-12-01T11:01:09.745640Z",
  "updated_at": "2025-12-01T11:01:10.986966Z",
  "credit_cost": 374,
  "message": "Job accepted. Poll /jobs/abc123-def456-ghi789 or await your webhook callback for results.",
  "artifact_url": null
}

Get Job Status

GET /jobs/:job_id

Check the status and progress of a processing job.

Python Example:

job_id = data["id"]
status_resp = requests.get(
    f"{API_BASE}/jobs/{job_id}",
    headers={"X-API-Key": API_KEY},
    timeout=30,
)
print("Status:", status_resp.json())

Node.js Example:

if (data.id) {
  const statusResponse = await fetch(`${API_BASE}/jobs/${data.id}`, {
    headers: { "X-API-Key": API_KEY },
  });
  console.log("Status:", await statusResponse.json());
}

Response (pending):

{
  "id": "abc123-def456-ghi789",
  "status": "pending",
  "step": "Waiting for worker",
  "webhook_url": null,
  "created_at": "2025-12-01T11:01:09.745640Z",
  "updated_at": "2025-12-01T11:01:10.986966Z",
  "credit_cost": 374,
  "message": null,
  "artifact_url": null
}

Response (running):

{
  "id": "abc123-def456-ghi789",
  "status": "running",
  "step": "Analyzing highlights",
  "webhook_url": null,
  "created_at": "2025-12-01T11:01:09.745640Z",
  "updated_at": "2025-12-01T11:06:07.341136Z",
  "credit_cost": 374,
  "message": null,
  "artifact_url": null
}

Response (completed):

{
  "id": "abc123-def456-ghi789",
  "status": "completed",
  "step": "Completed",
  "webhook_url": null,
  "created_at": "2025-12-01T11:01:09.745640Z",
  "updated_at": "2025-12-01T11:15:06.536314Z",
  "credit_cost": 374,
  "message": null,
  "artifact_url": "https://artifacts.viddyflow.com/runs/abc123-def456-ghi789/viddyflow_results_abc123-def456-ghi789.zip?...",
  "artifacts_expires_at": "2025-12-31T11:15:06.536314Z",
  "artifact_retention_days": 30
}

Status values: pending, running, completed, failed

Notice: The platform automatically calculates VOD duration. Download your results from artifact_url when status is completed.

Get Job Results

GET /jobs/:job_id/results

Get detailed information about generated highlight videos, shorts, and metadata. Returns only public-facing data - internal processing details like model configurations are filtered out.

Easiest path: read the top-level deliverables array. Each entry already pairs a clip's title/duration_seconds with its download url, so you don't need to cross-reference result_manifest and artifact_locations yourself. Use zip_url for the full archive.

Python Example:

job_id = data["id"]
results_resp = requests.get(
    f"{API_BASE}/jobs/{job_id}/results",
    headers={"X-API-Key": API_KEY},
    timeout=30,
)
results_resp.raise_for_status()
print("Results:", results_resp.json())

Node.js Example:

if (data.id) {
  const resultsResponse = await fetch(`${API_BASE}/jobs/${data.id}/results`, {
    headers: { "X-API-Key": API_KEY },
  });
  if (!resultsResponse.ok) {
    throw new Error(`Failed: ${resultsResponse.status}`);
  }
  console.log("Results:", await resultsResponse.json());
}

Response (summary):

{
  "id": "abc123-def456-ghi789",
  "status": "completed",
  "deliverables": [
    {
      "type": "reel",
      "title": "Audience highlight reel",
      "duration_seconds": 306.973,
      "url": "https://artifacts.viddyflow.com/runs/abc123/compilation.mp4?X-Amz-Signature=...",
      "run_name": "audience",
      "size_bytes": 45234567
    },
    {
      "type": "short",
      "index": 1,
      "title": "Epic Clutch Play",
      "description": "An unbelievable 1v3 clutch that sent chat into a frenzy.",
      "duration_seconds": 38.4,
      "tags": ["clutch", "gaming", "twitch"],
      "url": "https://artifacts.viddyflow.com/runs/abc123/shorts/1.mp4?X-Amz-Signature=...",
      "thumbnail_url": "https://artifacts.viddyflow.com/runs/abc123/shorts/1_cover.jpg?X-Amz-Signature=..."
    }
  ],
  "zip_url": "https://artifacts.viddyflow.com/runs/abc123/viddyflow_results_abc123.zip?X-Amz-Signature=...",
  "links_expire_at": "2025-12-31T11:15:06.536314Z",
  "result_manifest": {
    "summary": {
      "total_videos": 1,
      "generated_video_duration_seconds": 306.973,
      "total_shorts": 5,
      "thumbnails_generated": 3,
      "metadata_generated": 1,
      "source_vod_duration_seconds": 14590.0
    },
    "highlights": [
      {
        "story": "Epic Gameplay Moment",
        "start": 7846.0,
        "end": 7889.52,
        "duration": 43.52,
        "run_name": "audience"
      }
    ],
    "processed_videos": [
      {
        "run_name": "audience",
        "duration_seconds": 306.973,
        "thumbnail_count": 3
      }
    ],
    "shorts_count": 5
  },
  "shorts": [
    {
      "index": 1,
      "filename": "short_01_Epic_Clutch.mp4",
      "title": "Epic Clutch Play",
      "description": "An unbelievable 1v3 clutch that sent chat into a frenzy.",
      "duration_seconds": 38.4,
      "tags": ["clutch", "gaming", "twitch"],
      "cover": "short_01_Epic_Clutch_cover.jpg"
    },
    {
      "index": 2,
      "filename": "short_02_Chat_Goes_Wild.mp4",
      "title": "Chat Goes Wild",
      "description": "The moment the whole chat erupted.",
      "duration_seconds": 29.1,
      "tags": ["hype", "chat", "twitch"],
      "cover": "short_02_Chat_Goes_Wild_cover.jpg"
    }
  ],
  "artifact_locations": {
    "run_id": "abc123-def456-ghi789",
    "bucket": "viddyflow-runs",
    "region": "us-east-1",
    "url_expires_at": "2025-12-31T11:15:06.536314Z",
    "assets": {
      "compilation": {
        "url": "https://artifacts.viddyflow.com/runs/abc123-def456-ghi789/compilation.mp4?X-Amz-Signature=...",
        "key": "runs/abc123-def456-ghi789/compilation.mp4",
        "size_bytes": 45234567,
        "relative_path": "compilation.mp4"
      },
      "short": [
        {
          "url": "https://artifacts.viddyflow.com/runs/abc123-def456-ghi789/shorts/1.mp4?X-Amz-Signature=...",
          "key": "runs/abc123-def456-ghi789/shorts/1.mp4",
          "size_bytes": 12345678,
          "relative_path": "shorts/1.mp4"
        }
      ],
      "metadata": {
        "url": "https://artifacts.viddyflow.com/runs/abc123-def456-ghi789/metadata.json?X-Amz-Signature=...",
        "key": "runs/abc123-def456-ghi789/metadata.json",
        "size_bytes": 4567,
        "relative_path": "metadata.json"
      }
    },
    "zip_download_url": "https://artifacts.viddyflow.com/runs/abc123-def456-ghi789/viddyflow_results_abc123-def456-ghi789.zip?X-Amz-Signature=..."
  },
  "parameters": {
    "vod_url": "https://twitch.tv/videos/123456789",
    "streamer_name": "StreamerName",
    "game_name": "Game Name",
    "run_profiles": ["audience"],
    "model_preset": "rapid_sprint",
    "num_shorts": 5,
    "burn_captions": true
  },
  "created_at": "2025-12-01T11:01:09.745640Z",
  "updated_at": "2025-12-01T11:15:57.924024Z"
}

Structured Access: Individual files are available in artifact_locations.assets. Download compilation videos, shorts, thumbnails, and metadata separately via presigned CDN URLs. Each asset includes relative_path showing its location in the organized folder structure.

Bulk Download: All assets bundled in a single ZIP file available at artifact_locations.zip_download_url. All URLs expire after the time shown in url_expires_at (default: 6 hours).

Webhooks

When creating a job, you can provide a webhook_url to receive notifications when processing completes or fails.

Webhook Payload (completed):

{
  "event": "job.completed",
  "payload": {
    "job_id": "abc123-def456-ghi789",
    "deliverables": [
      {
        "type": "reel",
        "title": "Balanced highlight reel",
        "duration_seconds": 123.034,
        "url": "https://artifacts.viddyflow.com/runs/abc123/balanced/compilation/highlights_balanced.mp4?X-Amz-Signature=...",
        "run_name": "balanced",
        "size_bytes": 45234567
      },
      {
        "type": "short",
        "index": 1,
        "title": "Epic Clutch Play",
        "duration_seconds": 38.4,
        "tags": ["clutch", "gaming"],
        "url": "https://artifacts.viddyflow.com/runs/abc123/shorts/videos/short_01_Epic_Moment.mp4?X-Amz-Signature=..."
      }
    ],
    "zip_url": "https://artifacts.viddyflow.com/runs/abc123/viddyflow_results_abc123.zip?X-Amz-Signature=...",
    "links_expire_at": "2025-12-09T15:25:44.537163+00:00",
    "manifest": {
      "summary": {
        "total_videos": 2,
        "generated_video_duration_seconds": 307.725,
        "total_shorts": 9,
        "thumbnails_generated": 6,
        "metadata_generated": 2,
        "source_vod_duration_seconds": 8060.0
      },
      "highlights": [
        {
          "story": "Epic Gameplay Moment",
          "start": 7846.0,
          "end": 7889.52,
          "duration": 43.52,
          "run_name": "balanced"
        }
      ],
      "processed_videos": [
        {
          "run_name": "balanced",
          "duration_seconds": 123.034,
          "thumbnail_count": 3
        },
        {
          "run_name": "streamer",
          "duration_seconds": 184.691,
          "thumbnail_count": 3
        }
      ],
      "shorts_count": 9
    },
    "artifacts": {
      "assets": {
        "compilation": {
          "url": "https://artifacts.viddyflow.com/runs/abc123-def456-ghi789/balanced/compilation/highlights_balanced.mp4?X-Amz-Signature=...",
          "key": "runs/abc123-def456-ghi789/balanced/compilation/highlights_balanced.mp4",
          "size_bytes": 45234567,
          "relative_path": "balanced/compilation/highlights_balanced.mp4"
        },
        "short": [
          {
            "url": "https://artifacts.viddyflow.com/runs/abc123-def456-ghi789/shorts/videos/short_01_Epic_Moment.mp4?X-Amz-Signature=...",
            "key": "runs/abc123-def456-ghi789/shorts/videos/short_01_Epic_Moment.mp4",
            "size_bytes": 12345678,
            "relative_path": "shorts/videos/short_01_Epic_Moment.mp4"
          }
        ],
        "metadata": {
          "url": "https://artifacts.viddyflow.com/runs/abc123-def456-ghi789/balanced/compilation/balanced_metadata.json?X-Amz-Signature=...",
          "key": "runs/abc123-def456-ghi789/balanced/compilation/balanced_metadata.json",
          "size_bytes": 4567,
          "relative_path": "balanced/compilation/balanced_metadata.json"
        },
        "thumbnail": [
          {
            "url": "https://artifacts.viddyflow.com/runs/abc123-def456-ghi789/balanced/thumbnails/thumb_01.jpg?X-Amz-Signature=...",
            "key": "runs/abc123-def456-ghi789/balanced/thumbnails/thumb_01.jpg",
            "size_bytes": 234567,
            "relative_path": "balanced/thumbnails/thumb_01.jpg"
          }
        ]
      },
      "zip_download_url": "https://artifacts.viddyflow.com/runs/abc123-def456-ghi789/viddyflow_results_abc123-def456-ghi789.zip?X-Amz-Signature=...",
      "url_expires_at": "2025-12-09T15:25:44.537163+00:00"
    }
  }
}

Easiest path: the completed payload includes the same flat deliverables array (and zip_url) as GET /jobs/:id/results: read deliverables[].url directly, no correlation needed.

Individual Downloads: the payload also includes separate download links for each generated asset (compilation videos, shorts, thumbnails, metadata) under artifacts.assets. Each asset includes a presigned CDN URL, file size, and relative path organized by run type.

Bulk Download: A combined ZIP file containing all assets is available via zip_url (or artifacts.zip_download_url). All URLs expire after the time shown in links_expire_at.

Webhook Payload (failed):

{
  "event": "job.failed",
  "payload": {
    "job_id": "abc123-def456-ghi789",
    "error": "VOD no longer available"
  }
}

Every webhook body is wrapped as { "event", "payload" }. Events: job.running (payload { job_id }), job.completed, and job.failed. The failed payload may include extra context (e.g. shortfall_seconds) depending on the failure.

Rate Limits

Rate limit: 60 requests per minute per API key. Contact support if you need higher throughput.

Credit System

ViddyFlow uses a credit-based pricing model for transparent and flexible billing.

Processing Costs

Credits are consumed based on the source video duration and selected processing tier. For example, a 4-hour VOD processed with Rapid Sprint (18 credits/hour) costs approximately 72 credits base cost.

Purchase Credits

Credits can be purchased through monthly subscriptions or one-time top-ups. All plans include full API access and webhook support.

Support & Contact

Need help? We're here for you.

• Email: support@viddyflow.com

• Dashboard: app.viddyflow.com

• Contact: Contact us