Overview
Creates a new video project that will generate an AI-powered property tour video from a collection of images. The video project orchestrates multiple 5-second clips with smooth transitions and optional background music.
Authentication
Requires an authenticated user session. The video project is created in the user’s workspace.
Request Body
Name of the video project Example: "123 Main St Property Tour"
Optional description of the video project
Video aspect ratio. Options: 16:9, 9:16, 1:1
16:9 - Landscape (YouTube, desktop)
9:16 - Portrait (TikTok, Instagram Stories)
1:1 - Square (Instagram Feed)
ID of the background music track. Set to null for no music.
Music volume (0-100). Only applies if musicTrackId is set.
Whether to generate ambient audio for clips using AI
Array of clip configurations. Each clip represents a 5-second segment. URL of the source image for this clip
Optional ending image for smooth transitions. Falls back to sourceImageUrl if not provided.
Room type for AI context. Options: living-room, bedroom, kitchen, bathroom, exterior, etc.
Custom label like “Master Bedroom” or “Front Yard”
Position in the video sequence (0-indexed)
AI motion description. Example: “slow pan right, sunlight streaming through windows”
Transition style. Options: cut, seamless
Response
Current status: draft, generating, compiling, completed, failed
Estimated cost in cents. Formula: clips × $0.35 × 100
Trigger.dev run ID for progress tracking
Example Request
{
"name" : "Luxury Penthouse Tour" ,
"description" : "Downtown property showcase" ,
"aspectRatio" : "16:9" ,
"musicTrackId" : "track_modern_upbeat" ,
"musicVolume" : 60 ,
"generateNativeAudio" : true ,
"clips" : [
{
"sourceImageUrl" : "https://storage.supabase.co/images/living-room.jpg" ,
"endImageUrl" : "https://storage.supabase.co/images/living-room-alt.jpg" ,
"roomType" : "living-room" ,
"roomLabel" : "Main Living Area" ,
"sequenceOrder" : 0 ,
"motionPrompt" : "slow dolly forward, revealing city skyline" ,
"transitionType" : "seamless"
},
{
"sourceImageUrl" : "https://storage.supabase.co/images/kitchen.jpg" ,
"roomType" : "kitchen" ,
"roomLabel" : "Gourmet Kitchen" ,
"sequenceOrder" : 1 ,
"motionPrompt" : "pan across marble countertops" ,
"transitionType" : "seamless"
}
]
}
Example Response
{
"id" : "vp_abc123def456" ,
"workspaceId" : "ws_xyz789" ,
"userId" : "user_123" ,
"name" : "Luxury Penthouse Tour" ,
"description" : "Downtown property showcase" ,
"aspectRatio" : "16:9" ,
"musicTrackId" : "track_modern_upbeat" ,
"musicVolume" : 60 ,
"generateNativeAudio" : true ,
"status" : "draft" ,
"clipCount" : 2 ,
"completedClipCount" : 0 ,
"estimatedCost" : 70 ,
"finalVideoUrl" : null ,
"thumbnailUrl" : null ,
"durationSeconds" : null ,
"triggerRunId" : null ,
"triggerAccessToken" : null ,
"errorMessage" : null ,
"createdAt" : "2024-02-28T10:30:00Z" ,
"updatedAt" : "2024-02-28T10:30:00Z"
}
Next Steps
After creating the video project:
Trigger generation - Call the video generation endpoint to start processing
Track progress - Use the triggerRunId to monitor real-time progress
Download video - Once status is completed, download from finalVideoUrl
Compile Video Start video generation
Check Status Monitor progress
Video Generation Pipeline
Once generation starts, the system:
Generates clips - Creates 5-second AI videos in parallel (up to 3 at a time)
Creates transitions - Generates seamless transitions between clips
Compiles video - Uses FFmpeg to merge clips with audio
Uploads final video - Stores in Supabase Storage
Video generation takes approximately 2-5 minutes per clip . A 10-clip video takes about 20-30 minutes total.
Cost Calculation
// From source: lib/video/video-constants.ts
const CLIP_COST = 0.35 ; // USD per 5-second clip
const estimatedCost = clips . length × CLIP_COST × 100 ; // in cents
Example: 8 clips = 8 × 0.35 = 0.35 = 0.35 = 2.80 (280 cents)
Database Schema
// From source: lib/db/schema.ts:247-302
export const videoProject = pgTable ( "video_project" , {
id: text ( "id" ). primaryKey (),
workspaceId: text ( "workspace_id" ). notNull (),
userId: text ( "user_id" ). notNull (),
name: text ( "name" ). notNull (),
description: text ( "description" ),
aspectRatio: text ( "aspect_ratio" ). notNull (). default ( "16:9" ),
musicTrackId: text ( "music_track_id" ),
musicVolume: integer ( "music_volume" ). notNull (). default ( 50 ),
generateNativeAudio: boolean ( "generate_native_audio" ). notNull (). default ( true ),
status: text ( "status" ). notNull (). default ( "draft" ),
clipCount: integer ( "clip_count" ). notNull (). default ( 0 ),
estimatedCost: integer ( "estimated_cost" ). notNull (). default ( 0 ),
triggerRunId: text ( "trigger_run_id" ),
createdAt: timestamp ( "created_at" ). notNull (). defaultNow (),
});