{"openapi":"3.1.0","info":{"title":"agent-media API","version":"1.0.0","description":"AI UGC video production API. Generate talking head videos, SaaS reviews, and styled subtitles.","contact":{"url":"https://agent-media.ai"},"license":{"name":"Apache-2.0"},"termsOfService":"https://agent-media.ai/terms"},"servers":[{"url":"https://api.agent-media.ai","description":"Production API"}],"paths":{"/v1/generate/ugc_video":{"post":{"operationId":"ugc_video","summary":"Generate a UGC-style video from a script or prompt","tags":["generate"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"script":{"type":"string","minLength":50,"maxLength":3000},"prompt":{"type":"string","minLength":1,"maxLength":1000},"product_url":{"type":"string","format":"uri"},"actor_slug":{"type":"string","minLength":1,"maxLength":100},"persona_slug":{"type":"string","minLength":1,"maxLength":100},"face_photo_url":{"type":"string","format":"uri"},"voice":{"type":"string","maxLength":100},"target_duration":{"type":"number"},"style":{"type":"string","maxLength":50},"tone":{"type":"string","enum":["energetic","calm","confident","dramatic"]},"voice_speed":{"type":"number","minimum":0.7,"maximum":1.5},"music":{"type":"string","enum":["chill","energetic","corporate","dramatic","upbeat"]},"cta":{"type":"string","maxLength":100},"aspect_ratio":{"type":"string","enum":["9:16","16:9","1:1"]},"template":{"type":"string","maxLength":50},"composition_mode":{"type":"string","const":"pip"},"pip_options":{"type":"object","properties":{"position":{"type":"string","enum":["bottom-center","bottom-left","bottom-right"]},"size":{"type":"string","enum":["small","medium","large"]},"animation":{"type":"string","enum":["slide-up","slide-left","slide-right","fade","scale"]},"frame_style":{"type":"string","enum":["none","rounded","shadow"]}},"additionalProperties":false},"allow_broll":{"type":"boolean"},"broll_model":{"type":"string","enum":["kling3","hailuo2","wan21"]},"broll_images":{"type":"array","items":{"type":"string","format":"uri"},"maxItems":10},"product_image_url":{"type":"string","format":"uri"},"dub_language":{"type":"string","maxLength":10},"webhook_url":{"type":"string","format":"uri"},"scenes":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["talking_head","broll"]}},"additionalProperties":true},"maxItems":30}},"additionalProperties":false}}}},"responses":{"201":{"description":"Job submitted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobSubmitted"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient credits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsufficientCredits"}}}},"429":{"description":"Rate limited","headers":{"Retry-After":{"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/generate/saas_review":{"post":{"operationId":"saas_review","summary":"Generate a SaaS review video from a SaaS product URL","tags":["generate"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"script":{"type":"string","minLength":50,"maxLength":3000},"prompt":{"type":"string","minLength":1,"maxLength":1000},"product_url":{"type":"string","format":"uri"},"angle":{"type":"string","enum":["honest","enthusiastic","roast","tutorial","comparison"]},"actor_slug":{"type":"string","minLength":1,"maxLength":100},"persona_slug":{"type":"string","minLength":1,"maxLength":100},"face_photo_url":{"type":"string","format":"uri"},"voice":{"type":"string","maxLength":100},"target_duration":{"type":"number"},"style":{"type":"string","maxLength":50},"tone":{"type":"string","enum":["energetic","calm","confident","dramatic"]},"voice_speed":{"type":"number","minimum":0.7,"maximum":1.5},"music":{"type":"string","enum":["chill","energetic","corporate","dramatic","upbeat"]},"cta":{"type":"string","maxLength":100},"aspect_ratio":{"type":"string","enum":["9:16","16:9","1:1"]},"template":{"type":"string","maxLength":50},"composition_mode":{"type":"string","const":"pip"},"pip_options":{"type":"object","properties":{"position":{"type":"string","enum":["bottom-center","bottom-left","bottom-right"]},"size":{"type":"string","enum":["small","medium","large"]},"animation":{"type":"string","enum":["slide-up","slide-left","slide-right","fade","scale"]},"frame_style":{"type":"string","enum":["none","rounded","shadow"]}},"additionalProperties":false},"allow_broll":{"type":"boolean"},"broll_model":{"type":"string","enum":["kling3","hailuo2","wan21"]},"broll_images":{"type":"array","items":{"type":"string","format":"uri"},"maxItems":10},"dub_language":{"type":"string","maxLength":10},"webhook_url":{"type":"string","format":"uri"},"scenes":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["talking_head","broll"]}},"additionalProperties":true},"maxItems":30}},"additionalProperties":false}}}},"responses":{"201":{"description":"Job submitted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobSubmitted"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient credits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsufficientCredits"}}}},"429":{"description":"Rate limited","headers":{"Retry-After":{"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/generate/subtitle":{"post":{"operationId":"subtitle","summary":"Add styled subtitles to an existing video","tags":["generate"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"video_url":{"type":"string","format":"uri"},"style":{"type":"string","enum":["hormozi","minimal","bold","karaoke","clean","tiktok","neon","fire","glow","pop","aesthetic","impact","pastel","electric","boxed","gradient","spotlight"]}},"required":["video_url"],"additionalProperties":false}}}},"responses":{"201":{"description":"Job submitted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobSubmitted"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient credits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsufficientCredits"}}}},"429":{"description":"Rate limited","headers":{"Retry-After":{"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/generate/show_your_app":{"post":{"operationId":"show_your_app","summary":"Generate a video showing your app with an AI actor holding a phone","tags":["generate"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"app_screenshot_url":{"type":"string","format":"uri"},"actor_slug":{"type":"string","minLength":1,"maxLength":100},"script":{"type":"string","minLength":5,"maxLength":3000},"duration":{"type":"number","default":5},"subtitle_style":{"type":"string","enum":["hormozi","none"],"default":"hormozi"},"webhook_url":{"type":"string","format":"uri"}},"required":["app_screenshot_url","script"],"additionalProperties":false}}}},"responses":{"201":{"description":"Job submitted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobSubmitted"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient credits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsufficientCredits"}}}},"429":{"description":"Rate limited","headers":{"Retry-After":{"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/generate/product_acting_ugc":{"post":{"operationId":"product_acting_ugc","summary":"Generate a product-in-hand UGC video from a product image and actor reference","tags":["generate"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"product_image_url":{"type":"string","format":"uri","description":"Public URL of the product image to place in the actor scene. PNG, JPEG, or WebP recommended."},"actor_slug":{"type":"string","minLength":1,"maxLength":100,"description":"Actor slug from GET /v1/actors. This actor is used as the creator reference."},"actor_variant_id":{"type":"string","format":"uuid","description":"Optional actor variant UUID for a specific outfit, framing, expression, or background."},"product_name":{"type":"string","minLength":1,"maxLength":120,"description":"Optional product name used for prompt/script context."},"product_description":{"type":"string","minLength":1,"maxLength":1000,"description":"Product context used to generate a short script when script is omitted. Required when script is omitted."},"script":{"type":"string","minLength":5,"maxLength":3000,"description":"Exact words the actor should say. If omitted, the API generates a short script from product_description."},"template":{"type":"string","enum":["product-in-hand","mirror-selfie","bathroom-reaction","kitchen-counter","car-selfie","couch-review","expert-interview","product-closeup"],"default":"product-in-hand","description":"UGC scenario template for the generated frame and motion."},"acting_style":{"type":"string","enum":["raw-selfie","shocked","angry","excited","dramatic","weird-hook","casual-demo","honest-review"],"default":"raw-selfie","description":"Acting energy and delivery style."},"visual_style":{"type":"string","maxLength":200,"description":"Extra camera, pose, environment, or framing direction."},"duration":{"type":"number","default":5,"description":"Video duration in seconds. Valid values: 5, 10, or 15."},"subtitles":{"type":"boolean","default":true,"description":"Whether to burn word-level synced subtitles into the final video."},"subtitle_style":{"type":"string","enum":["hormozi","none"],"default":"hormozi","description":"Subtitle style. Use none to disable subtitle rendering."},"webhook_url":{"type":"string","format":"uri","description":"HTTPS URL to receive a callback when the job completes or fails."}},"required":["product_image_url","actor_slug"],"additionalProperties":false}}}},"responses":{"201":{"description":"Job submitted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobSubmitted"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient credits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsufficientCredits"}}}},"429":{"description":"Rate limited","headers":{"Retry-After":{"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/generate/laptop_ugc":{"post":{"operationId":"laptop_ugc","summary":"Generate a 3-scene laptop-UGC ad: actor holds laptop showing your app, scrolling B-roll, then a face-only selfie close","tags":["generate"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"app_screen_recording_url":{"type":"string","format":"uri","description":"Public URL of a screen recording of your app (mp4 or mov). Max 10 MB, max 10 seconds. Provide this OR app_screen_image_url, not both."},"app_screen_image_url":{"type":"string","format":"uri","description":"Public URL of a static screenshot of your app (png, jpeg, or webp). Max 5 MB. Provide this OR app_screen_recording_url, not both."},"actor_slug":{"type":"string","minLength":1,"maxLength":100,"description":"Actor slug from GET /v1/actors. Random actor if omitted."},"script":{"type":"string","minLength":5,"maxLength":3000,"description":"Full voiceover script. Auto-split at sentence boundary into two halves for the two face shots."},"duration":{"type":"number","default":20},"subtitle_style":{"type":"string","enum":["hormozi","none"],"default":"hormozi"},"webhook_url":{"type":"string","format":"uri"}},"required":["script"],"additionalProperties":false}}}},"responses":{"201":{"description":"Job submitted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobSubmitted"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient credits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsufficientCredits"}}}},"429":{"description":"Rate limited","headers":{"Retry-After":{"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/generate/character_video":{"post":{"operationId":"character_video","summary":"Generate a video of a character. Pick an actor by slug, OR pass a short description and we generate a character sheet via gpt-image-2 behind the scenes. Then Seedance 2.0 animates the reference image with your storyboard text.","tags":["generate"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"character_sheet_url":{"type":"string","format":"uri","description":"Public HTTPS URL of the character reference sheet PNG. Generate one via POST /v1/character/sheet-generate or upload your own."},"storyboard_url":{"type":"string","format":"uri","description":"Public HTTPS URL of the storyboard panels PNG. Generate one via POST /v1/character/storyboard-generate or upload your own."},"action_prompt":{"type":"string","minLength":1,"maxLength":2000,"description":"Optional scene/action description sent to Seedance as the primary scene driver (e.g. \"Marco the chef in his Brooklyn kitchen at golden hour, takes a bite of fresh bread\"). When omitted, the api-v2 server backstops it from the same session's storyboard job (script or beats) — without a real scene description Seedance picks the location arbitrarily."},"duration":{"type":"number","default":10,"description":"Video duration in seconds. Valid values: 5 or 10. Max 10s — Seedance i2v quality degrades past 10s with multimodal inputs."},"aspect_ratio":{"type":"string","enum":["9:16","16:9","1:1"],"default":"9:16","description":"Output aspect ratio. Default 9:16."},"generate_audio":{"type":"boolean","default":true,"description":"Whether Seedance synthesizes synchronized audio (ambient sounds, breath, etc.)."},"session_id":{"type":"string","format":"uuid","description":"Optional UUID linking the three pipeline steps (sheet → storyboard → video) into one wizard session. Same value across calls makes inserts idempotent and lets api-v2 backstop action_prompt from the storyboard step."},"webhook_url":{"type":"string","format":"uri","description":"HTTPS URL to receive a callback when the job completes or fails."}},"required":["character_sheet_url","storyboard_url"],"additionalProperties":false}}}},"responses":{"201":{"description":"Job submitted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobSubmitted"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient credits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsufficientCredits"}}}},"429":{"description":"Rate limited","headers":{"Retry-After":{"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/generate/text_to_video":{"post":{"operationId":"text_to_video","summary":"Pure text-to-video via Seedance 2.0 — no character, no storyboard, no actor. The prompt IS the whole creative (style, subject, mood, composition). Best for stylistic / scene-driven content where you want the model to invent everything from the prompt text alone.","tags":["generate"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"prompt":{"type":"string","minLength":20,"maxLength":1000,"description":"The full video prompt. Style, subject, mood, composition, lighting, lens, motion — all in one string. Seedance reads this verbatim. Min 20 / max 1000 chars."},"duration":{"type":"number","default":10,"description":"Video duration in seconds. Valid: 5, 10, or 15."},"aspect_ratio":{"type":"string","enum":["9:16","16:9","1:1"],"default":"9:16","description":"Output aspect ratio. 9:16 portrait (TikTok / Reels / Shorts), 1:1 square (feed posts), 16:9 landscape (YouTube / X / LinkedIn). Default 9:16."},"generate_audio":{"type":"boolean","default":true,"description":"Whether Seedance synthesizes synchronized audio. No extra charge."},"webhook_url":{"type":"string","format":"uri","description":"HTTPS URL to receive a callback when the job completes or fails."},"postiz_integration_ids":{"type":"array","items":{"type":"string"},"description":"Postiz integration IDs to auto-publish to once the video is rendered. Get them from GET /v1/integrations/postiz/accounts."},"caption_mode":{"type":"string","enum":["ai","static"],"default":"static","description":"Caption mode for auto-publish. 'static' (default) uses the literal `caption` string. 'ai' has Claude Opus generate one each time, biased by `caption_guidance`."},"caption":{"type":"string","minLength":1,"maxLength":2000,"description":"Literal caption text for the social post. Only used when caption_mode is omitted or \"static\"."},"caption_guidance":{"type":"string","minLength":1,"maxLength":1000,"description":"Tone / hashtag / length hints for the AI caption writer. Only used when caption_mode is \"ai\"."}},"required":["prompt"],"additionalProperties":false}}}},"responses":{"201":{"description":"Job submitted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobSubmitted"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient credits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsufficientCredits"}}}},"429":{"description":"Rate limited","headers":{"Retry-After":{"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/actors":{"get":{"operationId":"listActors","summary":"List actors, or look up a single actor by slug. Public endpoint.","tags":["actors"],"parameters":[{"name":"gender","in":"query","schema":{"type":"string","enum":["female","male"]},"description":"Filter by gender"},{"name":"age_range","in":"query","schema":{"type":"string","enum":["18-25","26-35","36-50","51+"]},"description":"Filter by age range"},{"name":"actor_type","in":"query","schema":{"type":"string","enum":["Young Adult","Professional","Mom","Elder","Casual"]},"description":"Filter by persona type"},{"name":"preset","in":"query","schema":{"type":"string"},"description":"Filter by preset tag (e.g. show_your_app)"},{"name":"search","in":"query","schema":{"type":"string"},"description":"Case-insensitive substring search on name"},{"name":"slug","in":"query","schema":{"type":"string"},"description":"Exact slug lookup. Returns a single actor or 404 with suggestions."},{"name":"limit","in":"query","schema":{"type":"integer","default":500,"minimum":1,"maximum":500},"description":"Page size. Default 500 — large enough to return the full active library in one call."},{"name":"offset","in":"query","schema":{"type":"integer","default":0,"minimum":0},"description":"Page offset."}],"responses":{"200":{"description":"Actor list (when slug is omitted) or single actor (when slug is given).","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ActorList"},{"$ref":"#/components/schemas/ActorSingle"}]}}}},"404":{"description":"Actor not found (slug lookup only)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","headers":{"Retry-After":{"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/videos/{jobId}":{"get":{"operationId":"getVideoStatus","summary":"Get video status","tags":["videos"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Job status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobStatus"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"API key (ma_xxx) or Supabase JWT"}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"array","items":{"type":"object"}}},"required":["code","message"]}},"required":["error"]},"InsufficientCredits":{"type":"object","properties":{"error":{"type":"string"},"error_description":{"type":"string"},"credits_required":{"type":"integer"},"credits_available":{"type":"integer"}},"required":["error","credits_required","credits_available"]},"JobSubmitted":{"type":"object","properties":{"job_id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["submitted"]},"estimated_duration":{"type":"integer"},"credits_deducted":{"type":"integer"},"selected_voice":{"type":"string"},"voice_auto_detected":{"type":"boolean"},"word_count":{"type":"integer"},"pacing_warning":{"type":"string","nullable":true}},"required":["job_id","status"]},"JobStatus":{"type":"object","properties":{"job_id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["submitted","processing","completed","failed"]},"progress":{"type":"object"},"video_url":{"type":"string","format":"uri","nullable":true},"error_message":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["job_id","status"]},"Actor":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"name":{"type":"string"},"gender":{"type":"string","enum":["female","male"]},"age":{"type":"integer"},"age_range":{"type":"string"},"nationality":{"type":"string"},"actor_type":{"type":"string"},"style":{"type":"string","description":"On-camera style descriptor."},"portrait_url":{"type":"string","format":"uri"},"green_screen_url":{"type":"string","format":"uri","nullable":true},"voice_id":{"type":"string","description":"ElevenLabs voice id used by default for this actor."},"voice_gender":{"type":"string"},"lip_sync_engine":{"type":"string"},"preset":{"type":"string","nullable":true},"presets":{"type":"array","items":{"type":"string"}},"bio_occupation":{"type":"string"},"bio_story":{"type":"string"},"bio_hobbies":{"type":"array","items":{"type":"string"}},"status":{"type":"string","enum":["active"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","slug","name","portrait_url","gender"]},"ActorList":{"type":"object","properties":{"actors":{"type":"array","items":{"$ref":"#/components/schemas/Actor"}},"total":{"type":"integer"},"limit":{"type":"integer"},"offset":{"type":"integer"}},"required":["actors","total","limit","offset"]},"ActorSingle":{"type":"object","properties":{"actor":{"$ref":"#/components/schemas/Actor"}},"required":["actor"]}}}}