Creative Director
Generate multi-scene creative sets from a single prompt using the Kolbo API.
The Creative Director generates multiple coordinated scenes from a single prompt. Give it a concept and scene count, and it produces a complete creative set — ideal for product showcases, storyboards, ad campaigns, and more.
All generation endpoints accept an optional
project_idbody field that routes the output into a specific project. See Projects.
Generate
Endpoint
POST /api/v1/generate/creative-directorRate limited to 30 requests/minute in production. The bucket is keyed by account (every API key you own shares it) and shared with the other non-image generation and write endpoints — video, music, speech, sound, 3D, video-to-video, lipsync, elements, first/last frame, chat, Visual DNA create/update, media upload and the cancel route all draw from the same 30/min counter. Requests that are rejected (any 4xx/5xx) do not consume the budget. Image generation and image editing use their own, separate 30/min bucket. Status polling is not rate limited. When this limiter trips the 429 body carries limit, window_seconds and retryAfter.
Creative Director adds a second, tighter guard on top of that shared bucket: batch starts are capped at 5 per minute per account and return 429 with code: "RATE_LIMIT_EXCEEDED". The SDK envelope only carries success, error and code for this one — the seconds until reset are stated inside the error message, not as separate fields.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
prompt | string | Yes | Creative concept or brief. Must be a non-empty string, otherwise 400 INVALID_PROMPT. Maximum 50,000 characters (400 PROMPT_TOO_LONG). #Name moodboard mentions inside the prompt are resolved against your library (an explicit moodboard_id wins). @Name Visual DNA mentions are resolved only when the pinned model supports Visual DNA — with Smart Select, pass the DNA in visual_dna_ids instead. |
scene_count | number | No | Number of scenes. Never rejected: 0 and any non-numeric value fall back to 4, a negative number clamps to 1, and anything above 8 clamps to 8 (default: 4). |
model | string | No | Model identifier from GET /api/v1/models, applied to every scene. Must be a string, otherwise 400 INVALID_MODEL. Omit it to let Smart Select pick per scene. An identifier that is not in the model catalogue is not rejected — Creative Director drops the pin and routes every scene through Smart Select instead, while the start response still echoes back the string you sent. |
aspect_ratio | string | No | Applied to every scene. Use a value from the chosen model's supported_aspect_ratios in GET /api/v1/models (default: "1:1"). |
visual_dna_ids | array of string | No | Visual DNA IDs for character/product consistency. Up to 20 IDs are read — extras are silently dropped, not rejected. Per-scene assignment is index-based: scene 1 gets ID 1, scene 2 gets ID 2, and any scene past the end of the array falls back to the first ID. Pass a single ID to apply the same DNA to every scene. If none of the IDs is accessible to you the start fails with 403 VISUAL_DNA_ACCESS_DENIED. |
reference_images | array of string | No | Reference image URLs. Image mode: they guide every scene. The cap is enforced silently after the batch starts, never as a 400: with no model pinned it is 8; with one pinned it is that model's max_reference_images from GET /api/v1/models (the server may apply a lower provider-side limit). Your images take priority; Visual DNA images fill whatever slots remain. Extra images are dropped. Video mode: each URL is attached to exactly one scene, in order (URL 1 → scene 1), so the array length must equal the effective scene_count, otherwise 400 SCENE_COUNT_IMAGE_MISMATCH. |
moodboard_id | string | No | A single Moodboard ID applied to all scenes. Must be a valid ObjectId, otherwise 400; an ID you cannot access returns 404 MOODBOARD_NOT_FOUND. |
moodboard_ids | array of string | No | Per-scene moodboard IDs, indexed from scene 1. A scene with no entry falls back to moodboard_id. Every element must be a valid ObjectId, otherwise 400. |
workflow_type | string | No | "image" or "video" (default: "image") |
duration | number | No | Seconds per scene, video mode only (default: 5). Use a value in the model's supported_durations, or within its min_output_duration-max_output_duration, from GET /api/v1/models. |
enhance_prompt | boolean | No | Accepted but inert on this endpoint. Creative Director always writes and enhances each scene's prompt itself — sending false does not turn that off. |
sound_enabled | boolean | No | Video mode only — generate native audio on models that support it. Ignored in image mode. Applies the model's sound_credit_multiplier to every scene (default: false). |
resolution | string | No | Resolution tier applied to every scene. Values are model-dependent — read supported_resolutions on the target model via GET /api/v1/models. Multiplied across all scenes, so a higher tier can substantially increase a batch's cost — see Credit Multipliers. |
font_ids | array of string | No | Image mode only. Up to 3 ready personal font family IDs (24-character hex) applied to every scene; duplicates are removed. Anything else (more than 3, a non-array, a malformed ID) is rejected with 400. With Smart Select, scenes are routed only to models with supports_custom_fonts: true in GET /api/v1/models; if you pin a model, pick one that supports custom fonts. |
project_id | string | No | Route the batch into a specific project. Defaults to your auto-created "API Generations" project. |
session_id | string | No | Add this batch to an existing Creative Director session of yours (the start response does not return one — look it up with GET /api/v1/sessions) instead of the daily API session. The project is taken from the session, so project_id is ignored. Invalid id → 400 SDK_SESSION_INVALID_ID; unknown, deleted or not a Creative Director session → 404 SDK_SESSION_NOT_FOUND. Write access is checked on the session's project, so the SDK_PROJECT_* errors below also apply. |
Creative Director checks credits twice before the batch starts. First a 5-credit floor: below it the request fails immediately with 403 INSUFFICIENT_CREDITS (body also carries balance, required and pricing_url). Then an estimate for the whole batch (scene count × model cost, × duration in video mode): if your balance cannot cover it the start fails with 403 INSUFFICIENT_CREDITS (or SUBSCRIPTION_INACTIVE), and a project with a budget that cannot cover it fails with 403 PROJECT_BUDGET_EXCEEDED. The real, per-scene cost is reserved afterwards by each scene.
Your account's active Color DNA palette is applied to the whole batch. Unlike the single-image and video endpoints, this route has no per-call opt-out — deactivate the palette first if a batch must ignore it.
Example
curl -X POST https://api.kolbo.ai/api/v1/generate/creative-director \
-H "X-API-Key: kolbo_live_..." \
-H "Content-Type: application/json" \
-d '{
"prompt": "Product showcase for a premium red sneaker",
"scene_count": 4,
"aspect_ratio": "16:9"
}'Response
A successful start returns HTTP 202 Accepted — the batch is queued, not finished. Every other generation endpoint returns 200, so do not test for 200 alone.
{
"success": true,
"generation_id": "6612f0a1b2c3d4e5f6a7b8c9",
"type": "creative_director",
"scene_count": 4,
"model": "smart_select",
"poll_url": "/v1/generate/creative-director/6612f0a1b2c3d4e5f6a7b8c9/status",
"poll_interval_hint": 5
}| Field | Type | Description |
|---|---|---|
generation_id | string | Batch id — poll and cancel with this |
scene_count | number | Scenes the batch actually planned |
model | string | The model you sent, or "smart_select" when you omitted it |
poll_url | string | Status path without the /api prefix — prepend https://api.kolbo.ai/api |
poll_interval_hint | number | Suggested seconds between polls |
Unlike the other generation endpoints, the Creative Director start response carries no session_id, project_id or credits_charged. Those fields belong to the shared start envelope; this route returns its own shape with exactly the keys shown above.
When Creative Director itself rejects the batch, the response is re-wrapped into { success: false, error, code } with the original HTTP status. code is the downstream code when there is one (see the table below), otherwise GENERATION_ERROR; any extra fields the downstream error carried are dropped. TRACKING_ERROR means the batch started but its id could not be recorded — in the 500 case the server cancels the batch for you, so nothing is left running that you cannot poll.
Errors
| Status | Body / code | Cause |
|---|---|---|
| 400 | INVALID_PROMPT | prompt missing, empty, or not a string |
| 400 | INVALID_MODEL | model sent as an array/object instead of a string identifier |
| 400 | Invalid moodboard_id format | moodboard_id is not a valid ObjectId |
| 400 | moodboard_ids must be an array | moodboard_ids sent as something other than an array |
| 400 | Invalid ID in moodboard_ids | One of the moodboard_ids entries is not a valid ObjectId |
| 400 | INVALID_FONT_IDS | Invalid font_ids (Select up to 3 font families / font_ids must be an array) |
| 400 | PROMPT_TOO_LONG | prompt longer than 50,000 characters |
| 400 | SCENE_COUNT_IMAGE_MISMATCH | Video mode: number of reference_images differs from the effective scene_count |
| 400 | NSFW_BLOCKED | The prompt was blocked by your account's content filter before anything was charged |
| 400 | SDK_PROJECT_INVALID_ID / SDK_SESSION_INVALID_ID | project_id / session_id is not a valid ObjectId |
| 403 | INSUFFICIENT_CREDITS | Balance below the 5-credit floor, or below the batch estimate, described above |
| 403 | SUBSCRIPTION_INACTIVE | The batch estimate check found no usable subscription |
| 403 | PROJECT_BUDGET_EXCEEDED | The target project's budget cannot cover the batch estimate |
| 403 | SDK_PROJECT_ACCESS_DENIED | You hold less than edit permission on that project |
| 403 | VISUAL_DNA_ACCESS_DENIED | None of the visual_dna_ids is accessible to you |
| 404 | SDK_PROJECT_NOT_FOUND / SDK_SESSION_NOT_FOUND | No project / Creative Director session with that id is visible to you |
| 404 | MOODBOARD_NOT_FOUND | moodboard_id does not exist or is not accessible |
| 404 | NOT_FOUND | A visual_dna_ids entry belongs to a restricted collection your key cannot use |
| 429 | RATE_LIMIT_EXCEEDED | More than 5 batch starts in a minute. The wait in seconds is in the error message |
| 429 | (no code) | The shared 30/min generation bucket is exhausted. Body carries limit, window_seconds, retryAfter |
| 500 | TRACKING_ERROR | The batch could not be tracked and was cancelled |
| 4xx | GENERATION_ERROR | Any other downstream Creative Director rejection that carries no code of its own |
Completion Contract
Polling is the only completion mechanism. Kolbo never calls you back. There is no webhook, no callback_url field on the request body, no server-sent events, and no route delivers outbound notifications. The batch id is registered as SDK-originated the moment the start response is written, so the shared progress emitter drops its events — but Creative Director's own per-scene emits write to Socket.IO directly and never consult that registry, so a socket you authenticate with an API key may still see scene chatter. Those events are the web app's internal transport: undocumented, unversioned and unsafe to build on. After the POST, loop against the status route below.
Read Polling & Cancellation for the shared concepts. Creative Director is the one batch flow that is partly on the generic contract — the generic status and cancel routes both accept a batch id — but its payload shape is its own:
| Generic generation contract | Creative Director |
|---|---|
Start returns 200 | Start returns 202 Accepted. Do not test for 200 alone. |
Start echoes session_id, project_id, credits_charged | None of the three. The start body is exactly the seven keys shown above. |
poll_url → /v1/generate/{id}/status | poll_url → /v1/generate/creative-director/{id}/status. The generic route also works: it recognises the batch and internally delegates to the dedicated one, returning a byte-identical body. |
One state for the whole job | state for the batch plus an independent status on every scene. Scene statuses are what carry the actionable detail. |
result.urls[] | scenes[].image_urls / scenes[].video_urls. There is no result object on this route. |
credits_used / credits_breakdown on completion | Never returned, even when state is completed — each scene is billed independently. Reconcile through Credits & Billing. |
failed means nothing was produced | A batch where some scenes failed still reports state: "completed" with usable URLs on the scenes that worked. Only a batch where the whole run collapsed reports failed. |
error explains the failure | error is the fixed string "Creative Director generation failed". The real reason is per scene. |
POST /v1/generate/{id}/cancel | Same route, and it is the only way to cancel a batch. Cancelling stops every scene still in flight. |
Batch lifecycle
pending → processing → completed (every scene finished, or some failed — check each scene)
→ failed (the batch itself collapsed)
→ cancelled (you called the cancel route)state is a normalized view of the underlying batch record, which has one extra value the API deliberately hides:
| Underlying batch status | state returned | Meaning |
|---|---|---|
pending | pending | Accepted, scenes not started |
generating | processing | At least one scene is running |
completed | completed | Every scene finished successfully |
partial | completed | Some scenes succeeded, some failed. Collapsed into completed on purpose — a partial batch is terminal and its successful scenes are ready. Detect it by looking for status: "failed" scenes. |
failed | failed | The batch could not produce anything |
cancelled | cancelled | Cancelled |
Anything the map does not recognise falls through to processing, so treat completed, failed and cancelled as your terminal set and keep polling on everything else.
Poll Status
Creative Director has a dedicated status endpoint that returns per-scene progress.
Endpoint
GET /api/v1/generate/creative-director/:id/statusThe generic GET /api/v1/generate/:generationId/status accepts a Creative Director id too — it delegates to this route and returns the identical body. See Polling & Cancellation.
Example
curl https://api.kolbo.ai/api/v1/generate/creative-director/6612f0a1b2c3d4e5f6a7b8c9/status \
-H "X-API-Key: kolbo_live_..."Response Fields
| Field | Type | Description |
|---|---|---|
state | string | pending | processing | completed | failed | cancelled. A batch where some scenes failed still reports completed — check each scene. |
progress | number | 0-100, computed as (completed + failed scenes) / total scenes |
scenes | array | One entry per scene, see below |
error | string | Present only when state is failed. Always the fixed string "Creative Director generation failed" — the actionable detail is on the individual scene entries. |
A Creative Director status body carries no credits_used or credits_breakdown, even when state is completed — those fields exist only on the generic status route for non-batch types. To reconcile what a batch cost, read the credit records from Credits & Billing.
Each scene carries:
| Field | Type | When | Description |
|---|---|---|---|
scene_number | number | always | 1-based scene index |
status | string | always | pending | processing | completed | failed. Scenes have no cancelled state of their own — cancelling the batch stops them, and the batch-level state reports it. |
title | string | null | always | AI-generated scene title |
progress | number | status: "processing" | 0-100 for that scene |
image_urls | array | completed image scenes | CDN image URLs |
video_urls | array | completed video scenes | CDN video URLs |
error | string | status: "failed" | Why that scene failed — the rest of the batch is unaffected |
Errors
There is no request body and no rate limiter on this route, so a normal poll loop is never throttled.
| Status | Body | Cause |
|---|---|---|
| 400 | Invalid generation ID format | :id is not a valid ObjectId |
| 404 | Generation not found | No Creative Director batch with that id belongs to your API key |
| 404 | Generation document not found | The batch was tracked but its underlying record no longer exists |
Response (In Progress)
{
"success": true,
"generation_id": "6612f0a1b2c3d4e5f6a7b8c9",
"type": "creative_director",
"state": "processing",
"progress": 50,
"scenes": [
{
"scene_number": 1,
"status": "completed",
"title": "Hero Shot",
"image_urls": ["https://media.kolbo.ai/images/..."]
},
{
"scene_number": 2,
"status": "completed",
"title": "Detail Close-up",
"image_urls": ["https://media.kolbo.ai/images/..."]
},
{
"scene_number": 3,
"status": "processing",
"title": "Lifestyle Context",
"progress": 60
},
{
"scene_number": 4,
"status": "pending",
"title": "Brand Statement"
}
]
}Response (Completed)
{
"success": true,
"generation_id": "6612f0a1b2c3d4e5f6a7b8c9",
"type": "creative_director",
"state": "completed",
"progress": 100,
"scenes": [
{
"scene_number": 1,
"status": "completed",
"title": "Hero Shot",
"image_urls": ["https://media.kolbo.ai/images/..."]
},
{
"scene_number": 2,
"status": "completed",
"title": "Detail Close-up",
"image_urls": ["https://media.kolbo.ai/images/..."]
},
{
"scene_number": 3,
"status": "completed",
"title": "Lifestyle Context",
"image_urls": ["https://media.kolbo.ai/images/..."]
},
{
"scene_number": 4,
"status": "completed",
"title": "Brand Statement",
"image_urls": ["https://media.kolbo.ai/images/..."]
}
]
}Response (Partial — some scenes failed)
The batch is terminal and reports state: "completed". The failed scene carries its own error and no URLs; every other scene is ready to use.
{
"success": true,
"generation_id": "6612f0a1b2c3d4e5f6a7b8c9",
"type": "creative_director",
"state": "completed",
"progress": 100,
"scenes": [
{
"scene_number": 1,
"status": "completed",
"title": "Hero Shot",
"image_urls": ["https://media.kolbo.ai/images/..."]
},
{
"scene_number": 2,
"status": "failed",
"title": "Detail Close-up",
"error": "Content policy: the prompt was rejected by the provider"
}
]
}state: "completed" does not mean every scene succeeded. Always filter scenes by status === "completed" before collecting URLs, and surface the error on any scene that failed. progress reaches 100 when completed + failed scenes equal the total, so a batch with failures still finishes at 100.
Response (Failed)
Only when the batch itself collapsed. error is the same fixed string every time:
{
"success": true,
"generation_id": "6612f0a1b2c3d4e5f6a7b8c9",
"type": "creative_director",
"state": "failed",
"progress": 0,
"scenes": [],
"error": "Creative Director generation failed"
}Note the HTTP status is still 200 and success is still true — a failed batch is a successful poll. Branch on state, never on the HTTP code. When scenes is non-empty, each entry's own error is the only place a real reason appears.
Video Mode
Generate coordinated video scenes instead of images by setting workflow_type to "video":
curl -X POST https://api.kolbo.ai/api/v1/generate/creative-director \
-H "X-API-Key: kolbo_live_..." \
-H "Content-Type: application/json" \
-d '{
"prompt": "30-second sneaker commercial with dynamic angles",
"scene_count": 4,
"workflow_type": "video",
"duration": 5,
"aspect_ratio": "16:9"
}'When using video mode, each completed scene returns video_urls instead of (or in addition to) image_urls in the status response.
To base each video scene on one of your own images, pass exactly one reference_images URL per scene (scene_count must equal the number of URLs). font_ids does not apply to video batches.
Add "sound_enabled": true to get native audio on video models that support it. It is applied per scene and multiplies each scene's cost by the model's sound_credit_multiplier.
Video batches are much slower than image batches — each scene is a full video generation running in parallel. Poll for tens of minutes rather than seconds, and use POST /api/v1/generate/:generationId/cancel if you need to stop a batch early.
Cancel a Batch
Creative Director batches use the shared cancel route. Cancelling the batch also stops every scene still in flight, and the response reports how many with batch_cancelled_count:
curl -X POST https://api.kolbo.ai/api/v1/generate/6612f0a1b2c3d4e5f6a7b8c9/cancel \
-H "X-API-Key: kolbo_live_..."batch_cancelled_count is omitted when no scene was still in flight. Cancel draws from the same 30/min generation bucket as the start endpoint, so cancelling a batch consumes one of the requests you could have spent starting one. A batch that already reached a terminal state returns 409 CANNOT_CANCEL with current_status — an answer, not a failure.
See Polling & Cancellation for the full contract.
With Reference Images
Pass reference images to guide the style and content of generated scenes:
curl -X POST https://api.kolbo.ai/api/v1/generate/creative-director \
-H "X-API-Key: kolbo_live_..." \
-H "Content-Type: application/json" \
-d '{
"prompt": "Product showcase in various lifestyle settings",
"scene_count": 4,
"reference_images": ["https://example.com/product-front.jpg", "https://example.com/product-side.jpg"],
"aspect_ratio": "1:1"
}'In image mode every reference image guides every scene. In video mode the array is split across scenes instead — the first URL is attached to scene 1, the second to scene 2, and so on — and the number of URLs must equal scene_count, otherwise the start fails with 400 SCENE_COUNT_IMAGE_MISMATCH.
With Visual DNA
Combine Creative Director with Visual DNA to generate consistent character or product scenes:
curl -X POST https://api.kolbo.ai/api/v1/generate/creative-director \
-H "X-API-Key: kolbo_live_..." \
-H "Content-Type: application/json" \
-d '{
"prompt": "Fashion lookbook for Emma in urban settings",
"scene_count": 6,
"visual_dna_ids": ["6601a1b2c3d4e5f6a7b8c9d0"],
"aspect_ratio": "9:16"
}'JavaScript Example
const API_KEY = "kolbo_live_..."; // Replace with your API key
async function generateCreativeDirector() {
const BASE = "https://api.kolbo.ai/api";
// Start generation (Smart Select picks the best model automatically)
const res = await fetch(`${BASE}/v1/generate/creative-director`, {
method: "POST",
headers: {
"X-API-Key": API_KEY,
"Content-Type": "application/json"
},
body: JSON.stringify({
prompt: "Product showcase for artisan coffee brand",
scene_count: 4,
aspect_ratio: "16:9"
})
});
const { generation_id, poll_url } = await res.json();
console.log(`Started generation: ${generation_id}`);
// Poll for results
let result;
do {
await new Promise(r => setTimeout(r, 5000));
const status = await fetch(`${BASE}${poll_url}`, {
headers: { "X-API-Key": API_KEY }
});
result = await status.json();
console.log(`Progress: ${result.progress}%`);
} while (result.state === "processing" || result.state === "pending");
// Collect all scene images
const allImages = result.scenes
.filter(s => s.status === "completed")
.flatMap(s => s.image_urls);
console.log(`Generated ${allImages.length} images across ${result.scenes.length} scenes`);
allImages.forEach(url => console.log(url));
}
generateCreativeDirector();Python Example
import requests
import time
API_KEY = "kolbo_live_..." # Replace with your API key
BASE = "https://api.kolbo.ai/api"
# Start generation (Smart Select picks the best model automatically)
r = requests.post(
f"{BASE}/v1/generate/creative-director",
headers={"X-API-Key": API_KEY},
json={
"prompt": "Storyboard for a 30-second sneaker commercial",
"scene_count": 6,
"aspect_ratio": "16:9"
}
)
gen = r.json()
print(f"Started generation: {gen['generation_id']}")
# Poll for results
while True:
time.sleep(5)
s = requests.get(
f"{BASE}{gen['poll_url']}",
headers={"X-API-Key": API_KEY}
).json()
completed = sum(1 for sc in s["scenes"] if sc["status"] == "completed")
print(f"Progress: {s['progress']}% ({completed}/{len(s['scenes'])} scenes)")
if s["state"] not in ("processing", "pending"):
break
# Print results
for scene in s["scenes"]:
if scene["status"] == "completed":
print(f"Scene {scene['scene_number']}: {scene['title']}")
for url in scene["image_urls"]:
print(f" {url}")With Moodboard
Apply a moodboard to all scenes:
curl -X POST https://api.kolbo.ai/api/v1/generate/creative-director \
-H "X-API-Key: kolbo_live_..." \
-H "Content-Type: application/json" \
-d '{
"prompt": "Product showcase for a premium sneaker",
"scene_count": 4,
"moodboard_id": "6601a1b2c3d4e5f6a7b8c9d0",
"aspect_ratio": "16:9"
}'Or use different moodboards per scene with moodboard_ids (one moodboard ID per scene, indexed from scene 1):
curl -X POST https://api.kolbo.ai/api/v1/generate/creative-director \
-H "X-API-Key: kolbo_live_..." \
-H "Content-Type: application/json" \
-d '{
"prompt": "Brand campaign across three distinct moods",
"scene_count": 3,
"moodboard_ids": ["MOODBOARD_ID_1", "MOODBOARD_ID_2", "MOODBOARD_ID_3"]
}'Replace MOODBOARD_ID_1, etc. with real IDs from GET /api/v1/moodboards.
Use GET /api/v1/moodboards to list available moodboards. See Moodboards for details.
Tips
- Omitting
modelenables Smart Select, which picks a model per scene. The start response echoes"model": "smart_select". Pass amodelidentifier fromGET /api/v1/modelsto pin every scene to one model. An identifier the catalog does not know returns400 MODEL_NOT_FOUND(with near-match suggestions) before the batch starts; auto-select aliases such as"auto"behave like omittingmodel. - Each scene is a full, independently billed generation, so batch cost scales with
scene_count— andresolution/sound_enabledmultipliers apply to every scene. - The AI generates the scene titles and per-scene prompts itself from your brief.
- Creative Director works well with Visual DNA for maintaining character consistency across scenes.
- Use
moodboard_idto apply a consistent style across all scenes, ormoodboard_idsfor per-scene styles.visual_dna_idsis indexed the same way. - Scenes run in parallel, so use the per-scene status to show progressive results in your UI instead of waiting for the whole batch.
- A scene that fails does not fail the batch — the batch still reaches
state: "completed"with that scene markedfailedand carrying its ownerror.