> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.empiriolabs.ai/generation-templates/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.empiriolabs.ai/_mcp/server. # Generation Templates Generation Templates includes two related surfaces: * **Effect templates**: pre-curated creative effects for `/v1/images/generations` and `/v1/videos/generations`. Pass `template: ""` and EmpirioLabs applies the tuned effect to a normal generation request. * **Compose recipes**: full-production short-form video workflows. Compose plans scenes, creates or selects visuals, generates voiceover, captions, optional music, and returns a finished MP4. The same catalog powers the **Templates** button in the [dashboard Playground](https://platform.empiriolabs.ai/dashboard/playground?templates=open), where Effects and Compose appear as top-level tabs. Everything on this page can also be run visually there, with live previews for each effect and recipe; open the [Compose tab](https://platform.empiriolabs.ai/dashboard/playground?templates=open\&compose=open) directly for the full-production recipes. ## List templates ### Request GET [https://api.empiriolabs.ai/v1/templates](https://api.empiriolabs.ai/v1/templates) ```curl curl https://api.empiriolabs.ai/v1/templates \ -H "Authorization: Bearer " ``` ```python import requests url = "https://api.empiriolabs.ai/v1/templates" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.empiriolabs.ai/v1/templates'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.empiriolabs.ai/v1/templates" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("Authorization", "Bearer ") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.empiriolabs.ai/v1/templates") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["Authorization"] = 'Bearer ' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://api.empiriolabs.ai/v1/templates") .header("Authorization", "Bearer ") .asString(); ``` ```php request('GET', 'https://api.empiriolabs.ai/v1/templates', [ 'headers' => [ 'Authorization' => 'Bearer ', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.empiriolabs.ai/v1/templates"); var request = new RestRequest(Method.GET); request.AddHeader("Authorization", "Bearer "); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": "Bearer "] let request = NSMutableURLRequest(url: NSURL(string: "https://api.empiriolabs.ai/v1/templates")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` Use the generic endpoint for the full image and video catalog, or the modality-specific endpoints when you already know the generation type. ```bash curl https://api.empiriolabs.ai/v1/templates?modality=image \ -H "Authorization: Bearer $EMPIRIOLABS_API_KEY" ``` ## List video templates ### Request GET [https://api.empiriolabs.ai/v1/videos/templates](https://api.empiriolabs.ai/v1/videos/templates) ```curl curl https://api.empiriolabs.ai/v1/videos/templates \ -H "Authorization: Bearer " ``` ```python import requests url = "https://api.empiriolabs.ai/v1/videos/templates" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.empiriolabs.ai/v1/videos/templates'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.empiriolabs.ai/v1/videos/templates" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("Authorization", "Bearer ") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.empiriolabs.ai/v1/videos/templates") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["Authorization"] = 'Bearer ' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://api.empiriolabs.ai/v1/videos/templates") .header("Authorization", "Bearer ") .asString(); ``` ```php request('GET', 'https://api.empiriolabs.ai/v1/videos/templates', [ 'headers' => [ 'Authorization' => 'Bearer ', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.empiriolabs.ai/v1/videos/templates"); var request = new RestRequest(Method.GET); request.AddHeader("Authorization", "Bearer "); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": "Bearer "] let request = NSMutableURLRequest(url: NSURL(string: "https://api.empiriolabs.ai/v1/videos/templates")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Filters * `category`: `viral`, `cinematic`, `motion`, `transform`, `social`, `extend`, `product`, `edit`, `portrait` * `modality`: `video` or `image` * `model`: only return templates that support a specific model slug * `featured`: `true` to filter to featured templates only ```bash curl https://api.empiriolabs.ai/v1/videos/templates?category=viral \ -H "Authorization: Bearer $EMPIRIOLABS_API_KEY" ``` ### Response shape ```json { "object": "list", "template_count": 11, "data": [ { "slug": "baseball-stadium", "display_name": "Stadium", "category": "viral", "description": "Customer-facing description shown in the playground card.", "recommended_model": "kling-o3", "supported_models": ["kling-o3"], "default_params": { "aspect_ratio": "16:9", "duration": 10 }, "required_inputs": { "image": true, "min_images": 1, "max_images": 1 }, "cover_image_url": "https://media.empiriolabs.ai/assets/template-posters/baseball-stadium.jpg", "preview_video_url": "https://media.empiriolabs.ai/assets/template-previews/baseball-stadium.mp4", "modality": "video", "is_featured": true, "display_order": 10 } ] } ``` ## List image templates ### Request GET [https://api.empiriolabs.ai/v1/images/templates](https://api.empiriolabs.ai/v1/images/templates) ```curl curl https://api.empiriolabs.ai/v1/images/templates \ -H "Authorization: Bearer " ``` ```python import requests url = "https://api.empiriolabs.ai/v1/images/templates" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.empiriolabs.ai/v1/images/templates'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.empiriolabs.ai/v1/images/templates" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("Authorization", "Bearer ") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.empiriolabs.ai/v1/images/templates") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["Authorization"] = 'Bearer ' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://api.empiriolabs.ai/v1/images/templates") .header("Authorization", "Bearer ") .asString(); ``` ```php request('GET', 'https://api.empiriolabs.ai/v1/images/templates', [ 'headers' => [ 'Authorization' => 'Bearer ', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.empiriolabs.ai/v1/images/templates"); var request = new RestRequest(Method.GET); request.AddHeader("Authorization", "Bearer "); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": "Bearer "] let request = NSMutableURLRequest(url: NSURL(string: "https://api.empiriolabs.ai/v1/images/templates")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ```bash curl https://api.empiriolabs.ai/v1/images/templates?model=seedream-5-0-lite \ -H "Authorization: Bearer $EMPIRIOLABS_API_KEY" ``` ## Compose Compose is the full-production video workflow. Use it when you want a complete short-form video from a brief, script, product idea, or explainable topic. Use effect templates when you want one curated effect applied to a normal image or video generation request. ### List Compose recipes ### Request GET [https://api.empiriolabs.ai/v1/videos/compose/recipes](https://api.empiriolabs.ai/v1/videos/compose/recipes) **`default`** ```curl default curl https://api.empiriolabs.ai/v1/videos/compose/recipes ``` **`default`** ```python default import requests url = "https://api.empiriolabs.ai/v1/videos/compose/recipes" response = requests.get(url) print(response.json()) ``` **`default`** ```javascript default const url = 'https://api.empiriolabs.ai/v1/videos/compose/recipes'; const options = {method: 'GET'}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` **`default`** ```go default package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.empiriolabs.ai/v1/videos/compose/recipes" req, _ := http.NewRequest("GET", url, nil) res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` **`default`** ```ruby default require 'uri' require 'net/http' url = URI("https://api.empiriolabs.ai/v1/videos/compose/recipes") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) response = http.request(request) puts response.read_body ``` **`default`** ```java default import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://api.empiriolabs.ai/v1/videos/compose/recipes") .asString(); ``` **`default`** ```php default request('GET', 'https://api.empiriolabs.ai/v1/videos/compose/recipes'); echo $response->getBody(); ``` **`default`** ```csharp default using RestSharp; var client = new RestClient("https://api.empiriolabs.ai/v1/videos/compose/recipes"); var request = new RestRequest(Method.GET); IRestResponse response = client.Execute(request); ``` **`default`** ```swift default import Foundation let request = NSMutableURLRequest(url: NSURL(string: "https://api.empiriolabs.ai/v1/videos/compose/recipes")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ```bash curl https://api.empiriolabs.ai/v1/videos/compose/recipes ``` The response includes public recipe metadata: * `slug`: pass this as `recipe` on `POST /v1/videos/compose` * `display_name`, `category`, `visual_mode`, and `audio_mode` * `scene_count`, `aspect_ratios`, and `default_caption_style` * `requires_face_image` and `accepts_product_image` * `models`: recommended and allowed model options by stage Model recommendations are read from the live Compose recipe catalog. If you override a stage model, use a value listed in that recipe's `models..options`. ### Plan a storyboard Use `mode: "plan"` to turn a topic or script into an editable storyboard. This is the fastest way to preview structure before spending on a full render. ### Request POST [https://api.empiriolabs.ai/v1/videos/compose](https://api.empiriolabs.ai/v1/videos/compose) **`default`** ```curl default curl -X POST https://api.empiriolabs.ai/v1/videos/compose \ -H "Authorization: Bearer " ``` **`default`** ```python default import requests url = "https://api.empiriolabs.ai/v1/videos/compose" headers = {"Authorization": "Bearer "} response = requests.post(url, headers=headers) print(response.json()) ``` **`default`** ```javascript default const url = 'https://api.empiriolabs.ai/v1/videos/compose'; const options = {method: 'POST', headers: {Authorization: 'Bearer '}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` **`default`** ```go default package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.empiriolabs.ai/v1/videos/compose" req, _ := http.NewRequest("POST", url, nil) req.Header.Add("Authorization", "Bearer ") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` **`default`** ```ruby default require 'uri' require 'net/http' url = URI("https://api.empiriolabs.ai/v1/videos/compose") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = 'Bearer ' response = http.request(request) puts response.read_body ``` **`default`** ```java default import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.empiriolabs.ai/v1/videos/compose") .header("Authorization", "Bearer ") .asString(); ``` **`default`** ```php default request('POST', 'https://api.empiriolabs.ai/v1/videos/compose', [ 'headers' => [ 'Authorization' => 'Bearer ', ], ]); echo $response->getBody(); ``` **`default`** ```csharp default using RestSharp; var client = new RestClient("https://api.empiriolabs.ai/v1/videos/compose"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", "Bearer "); IRestResponse response = client.Execute(request); ``` **`default`** ```swift default import Foundation let headers = ["Authorization": "Bearer "] let request = NSMutableURLRequest(url: NSURL(string: "https://api.empiriolabs.ai/v1/videos/compose")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ```bash curl https://api.empiriolabs.ai/v1/videos/compose \ -H "Authorization: Bearer $EMPIRIOLABS_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "recipe": "custom", "mode": "plan", "brief": "Explain why cold plunge clips went viral.", "aspect_ratio": "9:16", "scene_count": 5, "caption_style": "bold-white" }' ``` Compose returns an async job: ```json { "job_id": "job_01HV3KABCDE", "status": "processing", "poll_url": "/v1/jobs/job_01HV3KABCDE" } ``` Poll the job: ```bash curl https://api.empiriolabs.ai/v1/jobs/job_01HV3KABCDE \ -H "Authorization: Bearer $EMPIRIOLABS_API_KEY" ``` When the plan job completes, `result` contains a storyboard. You can edit that JSON before render. ### Render the video Send the storyboard back with `mode: "render"`. ```bash curl https://api.empiriolabs.ai/v1/videos/compose \ -H "Authorization: Bearer $EMPIRIOLABS_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "recipe": "custom", "mode": "render", "storyboard": { "title": "Why cold plunge clips took over", "aspect_ratio": "9:16", "caption_style": "bold-white", "scenes": [ { "index": 1, "narration": "Cold plunges look extreme, but the hook is simple.", "visual_prompt": "Fast vertical b-roll of ice, water, and a phone recording setup.", "duration_seconds": 4 } ] } }' ``` The final job result includes the output URL and Compose metadata. Generated media URLs expire after 7 days, so save anything you need to keep. ### Edit a render Use `mode: "edit"` when you already have a prior render manifest and want to change selected scenes. ```json { "recipe": "custom", "mode": "edit", "manifest": { "video_url": "https://media.empiriolabs.ai/worker-outputs/final.mp4", "scenes": [ { "index": 1, "clip_url": "https://media.empiriolabs.ai/worker-outputs/scene-1.mp4" } ] }, "edits": [ { "scene_index": 1, "instruction": "Make the caption hook shorter and punchier." } ] } ``` ### Common Compose fields | Field | Use | | ---------------------- | ------------------------------------------------------------------------------- | | `recipe` | Required. Recipe slug from `GET /v1/videos/compose/recipes`. | | `mode` | `plan`, `render`, or `edit`. | | `brief` | Topic, idea, or direction for plan mode. | | `script` | Optional script text for plan mode. | | `storyboard` | Required for render mode. Usually the completed plan result. | | `manifest` and `edits` | Required for edit mode. | | `aspect_ratio` | Common values include `9:16`, `16:9`, and `1:1`, depending on recipe support. | | `scene_count` | Requested scene count for plan mode. | | `caption_style` | Caption style ID from the recipes endpoint. | | `music` | Optional music direction or preset. | | `voice` | Optional voice preset or voice ID. | | `product_image` | Optional product reference image for recipes that accept one. | | `face_image` | Optional face or character reference for recipes that require one. | | `gameplay_style` | Optional gameplay background style when the recipe supports gameplay variants. | | `model_overrides` | Optional per-stage model overrides, such as `{ "visual": "seedance-2-0-pro" }`. | ### Compose usage and billing Compose records a parent usage row for the production, plus internal model legs for script, voice, visuals, transcription, music, and assembly when those stages run. The parent row is the customer-facing row returned by the Account Usage API. ```bash curl "https://api.empiriolabs.ai/v1/account/usage?source=compose&limit=20" \ -H "Authorization: Bearer $EMPIRIOLABS_API_KEY" ``` Compose rows use `source: "compose"` and include structured metadata: ```json { "source": "compose", "model": "custom", "endpoint": "/v1/videos/compose", "cost": { "amount": 0.1832, "currency": "USD" }, "metadata": { "category": "compose", "compose": { "recipe": "custom", "mode": "render", "phase": "render", "leg_count": 5, "total_cost_usd": 0.1832, "legs": [ { "stage": "voice", "model": "inworld-tts-mini", "cost_usd": 0.012 } ] } } } ``` Read `cost.amount` for the final debited amount. Use `metadata.compose.legs` when you want a breakdown of which model stages contributed. ## Fetch a single template ### Request GET [https://api.empiriolabs.ai/v1/templates/\{slug}](https://api.empiriolabs.ai/v1/templates/\{slug}) ```curl curl https://api.empiriolabs.ai/v1/templates/studio-product-shot \ -H "Authorization: Bearer " ``` ```python import requests url = "https://api.empiriolabs.ai/v1/templates/studio-product-shot" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.empiriolabs.ai/v1/templates/studio-product-shot'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.empiriolabs.ai/v1/templates/studio-product-shot" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("Authorization", "Bearer ") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.empiriolabs.ai/v1/templates/studio-product-shot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["Authorization"] = 'Bearer ' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://api.empiriolabs.ai/v1/templates/studio-product-shot") .header("Authorization", "Bearer ") .asString(); ``` ```php request('GET', 'https://api.empiriolabs.ai/v1/templates/studio-product-shot', [ 'headers' => [ 'Authorization' => 'Bearer ', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.empiriolabs.ai/v1/templates/studio-product-shot"); var request = new RestRequest(Method.GET); request.AddHeader("Authorization", "Bearer "); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": "Bearer "] let request = NSMutableURLRequest(url: NSURL(string: "https://api.empiriolabs.ai/v1/templates/studio-product-shot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ```bash curl https://api.empiriolabs.ai/v1/templates/studio-product-shot \ -H "Authorization: Bearer $EMPIRIOLABS_API_KEY" ``` Returns 404 with `code: "template_not_found"` if the slug doesn't exist. ## Generate a video with a template Add `template: ""` to a normal `/v1/videos/generations` call. You must provide whatever the template requires (`required_inputs`), usually a reference image. ```bash curl https://api.empiriolabs.ai/v1/videos/generations \ -H "Authorization: Bearer $EMPIRIOLABS_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "template": "baseball-stadium", "image_url": "https://example.com/me.jpg" }' ``` Behavior: * **Model selection**: if you don't pass `model`, the template's `recommended_model` is used. If you do, most templates validate that it is in `supported_models` and return 400 `template_model_unsupported` otherwise. Templates with `metadata.force_recommended_model: true` are pinned to `recommended_model` for effect fidelity. * **Modality check**: image templates only apply to `/v1/images/generations`; video templates only apply to `/v1/videos/generations`. * **Prompt blend**: your `prompt` (if any) is combined with the template's built-in styling so the generated output matches both your request and the effect's aesthetic. Send a short directional prompt; EmpirioLabs handles the rest. * **Default params**: `default_params` from the template merge in only for keys you didn't set explicitly. * **Required inputs**: `{ "image": true }` means the call returns 400 `template_missing_image` if no `image_url` / `image` / `images` is provided. ## Generate an image with a template ```bash curl https://api.empiriolabs.ai/v1/images/generations \ -H "Authorization: Bearer $EMPIRIOLABS_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "template": "background-swap", "prompt": "Place this product on a brushed steel studio plinth", "image": ["https://example.com/product.jpg"] }' ``` The response is the same async job envelope as a normal video generation: ```json { "job_id": "abc123...", "status": "processing", "poll_url": "/v1/jobs/abc123..." } ``` Poll `GET /v1/jobs/{job_id}` until terminal. ## Extend a prior video `/v1/videos/generations` also accepts `extend_from` to continue a previous generation. EmpirioLabs handles the prior-clip wiring for you and uses a sensible continuity prompt unless you provide your own. ```bash curl https://api.empiriolabs.ai/v1/videos/generations \ -H "Authorization: Bearer $EMPIRIOLABS_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "extend_from": { "job_id": "" } }' ``` You can also pass a direct URL: ```json { "extend_from": { "video_url": "https://media.empiriolabs.ai/..." } } ``` Extend can compose with templates: ```json { "template": "action-hero", "extend_from": { "job_id": "" } } ``` ### Extend works on every video model Pass `extend_from` with any supported video model. If you omit `model`, EmpirioLabs picks a sensible default for the extend. ## Error codes | HTTP | code | meaning | | ---- | ---------------------------- | ----------------------------------------------------------------------------- | | 400 | `template_not_found` | the slug doesn't match any active template | | 400 | `template_model_unsupported` | the model you passed isn't in the template's `supported_models` | | 400 | `template_modality_mismatch` | the template modality doesn't match the generation endpoint | | 400 | `template_missing_image` | the template requires an image but the body didn't include one | | 400 | `template_missing_video` | the template requires a reference video but the body didn't include one | | 400 | `template_no_model` | the template has no `recommended_model` and you didn't pass one | | 400 | `extend_extraction_failed` | couldn't process the prior video for extend. Try a different model. | | 400 | `extend_invalid_shape` | `extend_from` was malformed | | 400 | `extend_no_prior_video` | the prior job had no resolvable video URL | | 404 | `extend_prior_not_found` | the `job_id` in `extend_from` was unknown | | 500 | `extend_frame_upload_failed` | couldn't prepare the prior video for extend. Retry, or try a different model. | | 400 | `compose_unknown_recipe` | the `recipe` slug is not active | | 400 | `compose_bad_mode` | `mode` was not `plan`, `render`, or `edit` | | 400 | `missing_brief` | plan mode needs `brief` or script content | | 400 | `missing_storyboard` | render mode needs a storyboard | | 400 | `missing_manifest` | edit mode needs a prior render manifest | | 400 | `no_edits` | edit mode needs at least one edit instruction | | 401 | `unauthorized` | the request was missing a valid API key | | 402 | `insufficient_credits` | add credits before starting the job | | 504 | `compose_render_timeout` | the render exceeded the production budget. Try fewer or shorter scenes. | > One-field effects and Compose recipes for AI video. Apply curated effects to normal generations, or plan and render a full short-form production.