Kolbo.AIKolbo.AI Docs
Developer API

Developer API Overview

Programmatically generate images, videos, music, speech, and sound effects with the Kolbo API.

The Kolbo Developer API lets you programmatically access 100+ AI models for generating images, videos, music, speech, and sound effects. Use it from any language, or integrate directly into Claude Code via our MCP server.

Quick Start

These examples use the existing credit-billed API. Building with the separate USD wallet? Start with USD Console Quick Start. The key types and balances are not interchangeable.

1. Get an API Key

Create a key from the Developer Console or via the API:

curl -X POST https://api.kolbo.ai/api/api-keys \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "My App"}'

The key is in the fullKey field of the response (HTTP 201, body { status: true, data: { id, keyPrefix, name, permissions, createdAt, expiresAt }, fullKey, message }). Besides the required name (non-empty string), the body accepts permissions (array, optional, values read and write only — default ["read","write"]; anything else is a 400) and expiresInDays (number, optional, default 365, clamped to 1–730). Key management needs an interactive sign-in: calling these routes with an X-API-Key instead of a JWT returns 403 INTERACTIVE_AUTH_REQUIRED. Key creation is limited to 10 attempts per hour per user. If you lose the key you can re-reveal it with GET /api/api-keys/:keyId/reveal (same JWT auth, 30 requests/minute) or from the Developer Console.

2. Generate an Image

When you omit the model field, Kolbo uses Smart Select to automatically pick the best model for your prompt. This is the recommended approach for most use cases.

curl -X POST https://api.kolbo.ai/api/v1/generate/image \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "A sunset over mountains", "aspect_ratio": "16:9"}'

Response:

{
  "success": true,
  "generation_id": "abc123",
  "type": "image",
  "model": "auto",
  "credits_charged": 12,
  "poll_url": "/v1/generate/abc123/status",
  "poll_interval_hint": 3,
  "session_id": "68f...",
  "project_id": "68e..."
}

model echoes back what the server resolved your request to. Omitting model echoes "auto" on every Smart-Select-routed type (image, image edit, video, video-from-image, elements, first-last-frame, lipsync, chat, 3D); transcription always echoes "auto", and Creative Director echoes the model you sent or "smart_select". Video-to-video has no Smart Select: omitting model there returns 400 MODEL_REQUIRED. POST /v1/generate/music, /v1/generate/speech, and /v1/generate/sound instead substitute a concrete default identifier for that type and echo it (suno-v6, eleven_v4, and elevenlabs-sound-effects-v1 respectively; suno-v5.5 on /v1/generate/music/extend and /cover; music-v1 when the music body carries composition_plan, and on /v1/generate/music/edit-section). POST /v1/edit/image and POST /v1/edit/video echo the engine the edit resolved, falling back to the model you sent, or null.

An explicit model the catalog does not know is rejected with 400 MODEL_NOT_FOUND before the job starts. The message names near matches (Did you mean: …?) and points you to GET /v1/models?type=…. Canonical identifiers, registered aliases and provider-prefixed forms are accepted; a display name is not an identifier. Only an omitted, blank or auto-alias model gets the default described above. Video-to-video handles unknown identifiers itself: they are passed to its generation controller, which rejects them with its own error. Get valid identifiers from GET /v1/models.

credits_charged is the up-front estimate (it can be null); the real spend arrives on the status response. poll_interval_hint is seconds: 2 for chat, 3 for /v1/generate/image and /v1/generate/image-edit, 5 for sound and Creative Director, 8 for everything else (including /v1/edit/image). session_id and project_id tell you where the generation landed in the app (project_id is the auto-created "API Generations" project when you did not pass one).

3. Poll for Results

Replace abc123 with the generation_id from step 2:

curl https://api.kolbo.ai/api/v1/generate/abc123/status \
  -H "X-API-Key: YOUR_API_KEY"

When complete:

{
  "success": true,
  "generation_id": "abc123",
  "type": "image",
  "state": "completed",
  "progress": 100,
  "result": {
    "urls": ["https://cdn.kolbo.ai/..."],
    "thumbnail_url": "https://cdn.kolbo.ai/...",
    "model": "nano-banana-2",
    "prompt_used": "A breathtaking sunset...",
    "created_at": "2026-07-27T09:14:02.913Z"
  },
  "credits_used": 12,
  "credits_breakdown": [
    { "model": "nano-banana-2", "amount": 12 }
  ],
  "session_id": "68f...",
  "project_id": "68e..."
}

state is one of processing, completed, failed, or cancelled. result.model is always the model that actually ran — Smart Select's router id is never surfaced, so a request you submitted with model: "auto" comes back with the resolved identifier (or null if the generation record never recorded one), plus result.model_name when a display name was recorded. credits_used / credits_breakdown ([{ model, amount }]) are the real multiplier-adjusted spend; they appear only once state === "completed", and only when a charge was recorded for the generation. On state === "failed" you get error (a string) plus a structured failure object with message, category, code, retryable, and severity — every one of those sub-fields except message can be null, and a failed Creative Director batch returns error with no failure object at all. See Polling & Cancellation.

4. Detecting Success

Never branch on an exact HTTP status. The submit call is 2xx on success, but which 2xx differs per endpoint and per code path — the SDK passes through whatever the underlying generation handler answers, so 200, 201, and 202 Accepted are all in use (video trim, for example, answers 202), and an endpoint's code may change. A client that tests status === 200 will reject perfectly good jobs.

There is also no single success flag: one failure mode (TRACKING_ERROR, when a job started but its id could not be tracked) returns a 2xx with success: false, and errors from the global middleware and rate limiters carry status: false / message instead of success: false / error. The one check that holds everywhere is:

const started = await res.json();
if (!res.ok || started.success === false || started.status === false) {
  throw new Error(started.error ?? started.message ?? `HTTP ${res.status}`);
}

Then confirm you actually got a job handle — generation_id on every polled generation endpoint, message_id on POST /v1/chat, and jobId on video trim. The audio separation routes (/v1/audio/*) answer inline with their result and return no job handle. See Errors & Limits.

5. Using the Output URL

Treat output URLs as opaque and do not hardcode the host. Finished assets are served from Kolbo's CDN, and more than one host appears across these docs (media.kolbo.ai and cdn.kolbo.ai). Never allow-list, string-match, or reconstruct a host — read the exact string out of result.urls[] and use it verbatim.

The URLs in result are plain CDN links: they carry no query string, no signature, and none of your credentials. Fetch them with an ordinary GET and do not attach X-API-Key — that header belongs to api.kolbo.ai, not to the CDN host.

No lifetime is published for these URLs. Nothing in this API guarantees a finished asset stays reachable at the same address forever, and permanently deleting the media item (DELETE /v1/media/:id/permanent) removes the underlying file. If the asset matters to you, download the bytes into your own storage as soon as the generation completes, rather than persisting the Kolbo URL as your system of record.

If you lose a URL — or need one long after the poll loop ended — do not re-generate. Re-resolve it:

  • GET /v1/media/:id accepts a generation id as well as a media id, and returns that generation's media item (the first output, when the generation produced several). So the generation_id you kept is enough to find the asset again.
  • GET /v1/media?category=ai (plus project_id / type / search) lists everything the account has produced.

Both are documented in Media Library.

Full Example (JavaScript)

const API_KEY = "YOUR_API_KEY";
const BASE = "https://api.kolbo.ai/api/v1";

async function generateImage(prompt) {
  const res = await fetch(`${BASE}/generate/image`, {
    method: "POST",
    headers: {
      "X-API-Key": API_KEY,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({ prompt, aspect_ratio: "16:9" })
  });
  const started = await res.json();
  if (!res.ok || started.success === false || started.status === false) {
    throw new Error(started.error ?? started.message ?? `HTTP ${res.status}`);
  }
  const { generation_id } = started;

  while (true) {
    await new Promise((r) => setTimeout(r, 3000));
    const status = await fetch(`${BASE}/generate/${generation_id}/status`, {
      headers: { "X-API-Key": API_KEY }
    }).then((r) => r.json());

    if (status.state === "completed") return status.result.urls;
    if (status.state === "failed") throw new Error(status.error);
    // `cancelled` carries neither `result` nor `error`. Without this branch the
    // loop polls a cancelled generation forever.
    if (status.state === "cancelled") throw new Error("Generation was cancelled");
  }
}

generateImage("A sunset over mountains").then(console.log);

Full Example (Python)

import requests
import time

API_KEY = "YOUR_API_KEY"
BASE = "https://api.kolbo.ai/api/v1"

def generate_image(prompt):
    res = requests.post(
        f"{BASE}/generate/image",
        headers={"X-API-Key": API_KEY, "Content-Type": "application/json"},
        json={"prompt": prompt, "aspect_ratio": "16:9"}
    )
    started = res.json()
    if not res.ok or started.get("success") is False or started.get("status") is False:
        raise Exception(started.get("error") or started.get("message") or f"HTTP {res.status_code}")
    generation_id = started["generation_id"]

    while True:
        time.sleep(3)
        status = requests.get(
            f"{BASE}/generate/{generation_id}/status",
            headers={"X-API-Key": API_KEY}
        ).json()

        if status["state"] == "completed":
            return status["result"]["urls"]
        if status["state"] == "failed":
            raise Exception(status["error"])
        # `cancelled` carries neither `result` nor `error`. Without this branch the
        # loop polls a cancelled generation forever.
        if status["state"] == "cancelled":
            raise Exception("Generation was cancelled")

urls = generate_image("A sunset over mountains")
print(urls)

Generation Types

Every endpoint below returns immediately with a generation_id (POST /v1/chat calls the same handle message_id) and is polled at GET /v1/generate/:generation_id/status, with two exceptions. Audio Separation runs inline: the request stays open until the stems are ready and the response carries the result, with nothing to poll. Video Trim runs on its own job collection and is not resolvable on the generic status route. It returns no poll_url: Video Trim replies 202 with jobId (poll GET /v1/video/trim/:jobId — ignore the pollingUrl it also returns, which points at the in-app route, and the generationId field, which is the same trim job id, not a generation id). Creative Director is resolvable on it (the generic route detects the batch and internally delegates to the per-scene endpoint), but the dedicated GET /v1/generate/creative-director/:id/status path returns the same payload with one less hop. See Polling & Cancellation.

TypeEndpointDocs
ChatPOST /v1/chatChat
ImagePOST /v1/generate/imageImage Generation
Image Edit (prompt-driven)POST /v1/generate/image-editImage Editing
Image Edit (targeted operations)POST /v1/edit/imageImage Editing
Video (text)POST /v1/generate/videoVideo Generation
Video (from image)POST /v1/generate/video/from-imageVideo Generation
Video (from video)POST /v1/generate/video-from-videoVideo to Video
Video edit operationsPOST /v1/edit/videoVideo Editing
Elements (ref → video)POST /v1/generate/elementsElements
First-Last FramePOST /v1/generate/first-last-frameFirst-Last Frame
LipsyncPOST /v1/generate/lipsyncLipsync
MusicPOST /v1/generate/musicMusic Generation
Music extend / cover (from existing audio)POST /v1/generate/music/extend, POST /v1/generate/music/cover (source via upload_id from POST /v1/generate/music/import, or audio_url)Music Generation
Music section edit (structured-song tracks)POST /v1/generate/music/edit-sectionMusic Generation
SpeechPOST /v1/generate/speechSpeech & Sound
Sound EffectsPOST /v1/generate/soundSpeech & Sound
Creative Director (preferred poll path: GET /v1/generate/creative-director/:id/status)POST /v1/generate/creative-directorCreative Director
3D ModelPOST /v1/generate/3d3D Generation
TranscriptionPOST /v1/transcribeTranscription
Audio Separation (Dialogue / Music / Effects stems) — inline, no pollingPOST /v1/audio/separate (+ /clean-dialogue, /ambience)Audio Separation
Video TrimPOST /v1/video/trimVideo Editing

Rate limits differ by endpoint. Limits are per user account per minute (keys of the same account share them), and a request that fails does not count against the window.

  • Image endpoints (/v1/generate/image, /v1/generate/image-edit, /v1/edit/image): 30/minute, in their own bucket.
  • Every other generation write (video, music including import/extend/cover/reference-audio/edit-section, speech, sound, elements, first-last-frame, lipsync, video-to-video, 3D, Creative Director, chat, video analysis, /v1/edit/video, video trim, cancel, media upload, voice clone/import, Visual DNA create/update, character sheet, skill create/update/delete, lipsync-plan writes): 30/minute, shared.
  • /v1/transcribe: 100/minute.
  • Media library, folders, sessions, AI Docs, Review, moodboards and color palettes: 120/minute; bulk media and session move/split/undo routes: 20/minute.
  • Audio separation (/v1/audio/separate, /clean-dialogue, /ambience): 10/minute.
  • Video Editor session create and export, URL download start, and the two paid Music Library acquisition routes: 5/minute each.

A 429 carries limit, window_seconds, and retryAfter (seconds). See Errors & Limits.

Visual DNA

Create reusable visual identities for characters, products, or styles, then attach them to any generation for consistency.

EndpointDescription
POST /v1/visual-dnaCreate a Visual DNA from reference images
GET /v1/visual-dnaList your Visual DNAs
GET /v1/visual-dna/:idGet Visual DNA details
PUT /v1/visual-dna/:idUpdate a Visual DNA in place (name, description, stills, sheet)
DELETE /v1/visual-dna/:idDelete a Visual DNA
POST /v1/visual-dna/:id/importCopy a global preset (no body) or a collaborator's Visual DNA (body project_id, required for collaborator imports) into your own library. Free

See Visual DNA for details.

Moodboards

Discover and apply style templates (moodboards) to guide the visual direction of your generations.

EndpointDescription
GET /v1/moodboardsList available moodboards (personal + presets)
GET /v1/moodboards/:idGet moodboard details

Create, update, and delete moodboards with POST /v1/moodboards, PUT /v1/moodboards/:id, and DELETE /v1/moodboards/:id (image files sent as multipart are capped at 15 per request, 10 MB each, images only). Pass moodboard_id to image generation, image editing, or Creative Director requests (Creative Director also accepts several as moodboard_ids). See Moodboards for details.

Color DNA

A saved color palette that, once active, color-grades your generations automatically. Color DNA is account-wide: one palette at a time is the account's active palette, and it stays on until you deactivate it or activate another. A project is never required; passing one only records where the palette was born (which shares it with that project's edit/full members) and points the project's showcase at it.

EndpointDescription
GET /v1/color-palettesList your palettes (personal + active-organization scope, plus palettes born in the project you name). Query: page (default 1), limit (default 50, clamped to 1–100), projectId or project_id (optional). Response: color_palettes[], presets[] (system Color DNAs, not paginated), pagination. is_active marks the account's active palette
POST /v1/color-palettesCreate a palette. Body: name (string, required, 1–100), colors (array, required, 1–10 of { hex: "#RRGGBB" required, name ≤60 chars, role: dominant | secondary | accent | background }), source_image_urls (≤5 URLs), is_active (bool, default true — the new palette becomes the account's active palette unless you send false), projectId / project_id (string, optional), shareWithOrganization (bool, optional, opt-in). Any other body key is rejected with 400
PUT /v1/color-palettes/:idRename and/or replace colors / source_image_urls (owner only, never changes which palette is active). Any other body key is rejected with 400
DELETE /v1/color-palettes/:idDelete a palette you own; if it was active, Color DNA turns off, and it is cleared from every project that pointed at it
POST /v1/color-palettes/:id/activateMake this the account's active palette. Body: projectId / project_id (optional — only also points that project's showcase at it, when you can edit the project)
POST /v1/color-palettes/deactivateTurn Color DNA off for the account. No body needed (projectId is ignored)
POST /v1/color-palettes/analyzeExtract colors from 1–5 image URLs. Body: image_urls (array, required, 1–5). Free, saves nothing, and has an extra limit of 10 requests/minute

skip_color_palette: true opts one request out of the active palette without deactivating it. Only four handlers read it: POST /v1/generate/image, /v1/generate/image-edit, /v1/generate/video, and /v1/generate/video/from-image. Other generation endpoints ignore the field.

See Color Palettes for details.

Cinematic Presets

EndpointDescription
GET /v1/cinematic-presetsCinematic preset ids grouped by dimension. The response wraps the grouped map in a dimensions object — { success: true, dimensions: { "<dimension>": [{ id, name, description, thumbnail_url, preview_url?, ui_hint?, sort_order, bundle? }, …] } } — and sets no ETag (an environment without the catalog returns an empty dimensions). The dimension keys are data-driven (the catalog can serve looks, camera, lens, focal_length, aperture, angle, shot_type, lighting, color_palette, genre, tempo, moveset; only dimensions with active presets appear), so read them from the response rather than hardcoding

Pass the ids you want in the optional cinematic object on POST /v1/generate/image and POST /v1/generate/image-edit, keyed by dimension. looks entries are combo recipes, not a dimension you pass: expand a look's bundle ({ <dimension>: <preset id> }) into the cinematic object instead. See Cinematic Presets.

Music Library

Search Kolbo's catalog of licensed, ready-made background tracks (distinct from music generation). Discovery is free; the two acquisition endpoints are paid.

EndpointDescription
POST /v1/music-library/searchKeyword search + genre/mood/bpm/duration filters
POST /v1/music-library/analyze-scriptAI: turn a script into a music search
GET /v1/music-library/catalogBrowse the catalog (paginated)
GET /v1/music-library/facetsAvailable genres, moods, instruments + ranges
GET /v1/music-library/track/:id/audio128/320/WAV URLs — watermarked previews unless the account has clean access (a subscription that includes it, or a prior purchase of this track)
GET /v1/music-library/track/:id/relatedStems + alternate versions, gated the same way
GET /v1/music-library/track/:id/lyricsLyrics text + theme + explicit flag
POST /v1/music-library/clean/:trackIdPaid — acquire a clean, unwatermarked MP3/WAV. Body: requestId (required, 8–80 chars of A-Z a-z 0-9 _ -), format (mp3 default | wav), optional stem (a stem ref from /related), purpose (download | timeline), projectId
POST /v1/music-library/importPaid — acquire a clean file and copy it into the media library. Body: trackId (required), format (mp3 | wav), optional requestId, projectId

The two acquisition endpoints charge on the first call (accounts without clean access buy the track with credits), with no confirmation step, and require a write-enabled API key — a read-only key gets 403 API_KEY_READ_ONLY. They are idempotent through the requestId you supply, so send a stable one when retrying; on /import an omitted requestId is generated server-side, but a track already in your media library is returned as-is (alreadyImported: true) instead of being bought again. Both share a 5/minute rate limit, much tighter than the 60/minute discovery endpoints.

See Music Library for details.

Stock Library

Unified, multi-source stock media — photos, videos, illustrations, vectors, 3D models, sound effects, and music. Find b-roll, references, and project assets. All endpoints are free (no credits).

Sources reachable from the API: kolbo-ai (Kolbo's own AI sound effects, music, and images), pexels, unsplash, pixabay, coverr, freesound, sketchfab. Call GET /v1/stock/sources for the live list and each source's supported media types and filters.

The licensed production-music source (music, and its internal alias synci) is not exposed on the stock endpoints. GET /v1/stock/search?source=music and GET /v1/stock/asset/music/:id return 404 STOCK_NOT_FOUND; GET /v1/stock/sources filters the source out of its list, and GET /v1/stock/categories?source=music returns an empty 200 ({ success: true, categories: [], count: 0 }). GET /v1/stock/collections only ever serves Kolbo's own SFX collections (mediaType must be sfx; anything else returns an empty list). Reach the licensed catalog through the Music Library endpoints instead.

EndpointDescription
GET /v1/stock/sourcesList enabled sources + supported media types/filters
GET /v1/stock/categoriesDynamic category/topic chips per source
GET /v1/stock/collectionsKolbo SFX category collections + themed packs
GET /v1/stock/searchUnified search (source=all interleaves providers)
GET /v1/stock/asset/:source/:idOne asset + download variants + author/license
POST /v1/stock/analyze-scriptAI: turn a script into b-roll search terms
POST /v1/stock/importCopy an asset into the media library (CDN copy)

See Stock Library for details, and Asset Library License & Terms of Use for the license governing anything you import.

Chat

Send messages to any Kolbo chat model with multi-turn conversation support. Call GET /v1/models?type=chat for the current list of chat model identifiers.

EndpointDescription
POST /v1/chatSend a chat message (requires polling)
GET /v1/chat/conversationsList your conversations
GET /v1/chat/conversations/:id/messagesGet conversation messages

See Chat for details.

Media Library

Upload local files to the user's Kolbo library, get back a stable CDN URL, and browse everything the user has — uploaded files and AI-generated outputs — with the same filters as the desktop app and Adobe plugin (by project, folder, type, and section).

EndpointDescription
POST /v1/media/uploadUpload a file (multipart field file, up to 500 MB) and receive a stable URL. A source_url body is rejected with 400 — fetch the bytes yourself and send them as the file
POST /v1/media/upload-ticketMint a short-lived upload-only ticket (token, upload_url, expires_in, max_file_mb) for browser upload flows that cannot hold an API key
GET /v1/mediaList media — filter by project_id, folder_id, session_id / session_ids, preset_id, moodboard_id, type (all | image | video | audio), category (section: all | ai | uploaded | edited | favorites | training-lab), source_type, source, search, sort, include_metadata, pagination (page, page_size — capped at 200). An unknown type / category / source_type / sort value is a 400. include_deleted=true is the only way to list trashed items; no MCP tool exposes it
GET /v1/media/:idFetch one media item (full metadata)
DELETE /v1/media/:idSoft-delete (30-day trash)
POST /v1/media/:id/restoreRestore from trash
DELETE /v1/media/:id/permanentPermanent delete (NOT reversible)
PATCH /v1/media/:id/projectMove item to a different project
POST /v1/media/bulk/deleteBulk soft-delete (≤1000)
POST /v1/media/bulk/restoreBulk restore (≤1000)
POST /v1/media/bulk/permanentBulk permanent delete (NOT reversible, ≤1000)
POST /v1/media/bulk/moveBulk move to project (partial success, ≤500)
GET /v1/media/statsItem counts + total storage bytes
POST /v1/media/:id/favoriteMark a media item as favorited (idempotent)
DELETE /v1/media/:id/favoriteRemove a media item from favorites (idempotent)
GET /v1/media/foldersList the user's media folders (owned + shared)
POST /v1/media/foldersCreate a folder
PUT /v1/media/folders/:idRename / recolor / re-icon a folder (owner only)
DELETE /v1/media/folders/:idSoft-delete a folder (owner only)
POST /v1/media/folders/:id/itemsAdd media items to a folder
DELETE /v1/media/folders/:id/itemsRemove media items from a folder
POST /v1/media/folders/:id/shareShare a folder by user email (owner only)
DELETE /v1/media/folders/:id/share/:userIdRevoke folder access (owner only)
POST /v1/media/folders/:id/move-contentsMove every item in a folder to a project

See Media Library for the full filter reference and examples.

Presets Discovery

EndpointDescription
GET /v1/presetsList generation presets as { success, presets[], count }. Optional type filter: image, image_edit (a separate catalog — its ids are the valid preset_id values for /v1/generate/image-edit and are not interchangeable with image ids), video, music, or text_to_video (alias of video). Omit type and every catalog is returned in one flat presets[], each row tagged with its own type (video presets then appear twice, once as video and once as text_to_video). An unrecognized type returns an empty list, not an error.

Projects

Every generation endpoint accepts an optional project_id body field that routes the generation into a specific project. See Projects.

EndpointDescription
GET /v1/projectsList projects you can write into (owned + shared with edit/full/owner). Query: page (default 1), limit (1–200, default 50), include_archived=true. is_default: true marks the "API Generations" fallback project
PATCH /v1/sessions/:sessionId/projectMove a session (any type) with all its generations and media to another project
POST /v1/sessions/moveMove up to 100 sessions into one project in a single call
GET /v1/sessions/:sessionId/generationsList a session's generations as groups (prompt + all its outputs)
POST /v1/sessions/:sessionId/generations/moveMove selected generations into another existing session
POST /v1/sessions/:sessionId/splitSplit selected generations into a new session
POST /v1/sessions/organize/undo/:operationIdUndo a move or split (15-minute window)
POST/GET /v1/docs, GET/PUT/DELETE /v1/docs/:id, PATCH /v1/docs/:id/shareAI Docs (Magic Pad): author, edit, and share project-scoped documents
GET/POST /v1/review/* (assets, collections, comments, share-links)Kolbo Review: Frame.io-style client review — versions, status, comments, guest links
POST /v1/visual-dna/character-sheetGenerate a multi-angle character sheet for stronger character consistency (credits)
GET/POST /v1/visual-dna/folders, PUT/DELETE /v1/visual-dna/folders/:folderId, PUT /v1/visual-dna/:id/folderVisual DNA folders: organize characters (contents move to root on delete)
POST /v1/projects, GET /v1/projects/:id, PUT /v1/projects/:id, PUT /v1/projects/:id/archive, PUT /v1/projects/:id/unarchiveProject lifecycle: create, read (full description), rename/describe, archive, unarchive (deletion stays in-app)
POST /v1/projects/:projectId/duplicateDuplicate a project you own
POST /v1/projects/:id/transfer, GET /v1/project-transfers, POST /v1/project-transfers/:id/accept, POST /v1/project-transfers/:id/decline, DELETE /v1/project-transfers/:idTransfer project ownership to another user, and accept / decline / cancel pending transfers
GET /v1/sessionsList sessions across all types (filter by project/type)
PATCH /v1/sessions/:sessionId, DELETE /v1/sessions/:sessionId, POST /v1/sessions/:sessionId/restoreRename, soft-delete, and restore a session
POST /v1/projects/:projectId/context/url, POST /v1/projects/:projectId/context/text, GET /v1/projects/:projectId/context, DELETE /v1/projects/:projectId/context/:fileKeyProject knowledge base (RAG) sources
GET /v1/projects/:projectId/profile, POST /v1/projects/:projectId/profile/regenerateSynthesized living project profile
GET /v1/projects/:projectId/assets, POST .../assets/link, PUT .../assets/:type/:id/note, DELETE .../assets/:type/:idProject cast: list / tag / untag Visual DNAs and moodboards; per-asset purpose notes

Skills (Custom Chat Agents)

Reusable named personas for chat. The skill's description is the system instruction the model adopts. Every route exists under both /v1/skills (current name) and /v1/agents (original path, kept working) with identical behavior.

EndpointDescription
GET /v1/skills (or /v1/agents)List your skills (personal + global presets)
POST /v1/skills (or /v1/agents)Create a skill
PUT /v1/skills/:id (or /v1/agents/:id)Update a skill (personal skills only — global presets are protected)
DELETE /v1/skills/:id (or /v1/agents/:id)Delete a skill

See Agents & Sessions for details.

Voices

EndpointDescription
GET /v1/voicesList TTS voices (presets + your cloned voices)
POST /v1/voices/cloneClone a voice from an audio sample (multipart body with voiceName (required) and file fields audioFile, optional promptAudioFile and avatarFile, 25 MB each; charges credits)
POST /v1/voices/import-elevenlabsImport an existing ElevenLabs voice by id
DELETE /v1/voices/:idDelete a custom voice

Video Trim

EndpointDescription
POST /v1/video/trimFrame-accurate trim of a Kolbo-hosted video. Body: video_url (required), start_time and end_time (required, numbers in seconds — strings are a 400), project_id (optional). Returns 202 with a job id
GET /v1/video/trim/:jobIdPoll the trim job (trim jobs are not on /v1/generate/:id/status)

See Video Editing and Trim for details.

HTML Artifacts

Publish a self-contained HTML page, SVG, or Mermaid document to a public URL.

This one is not under /api/v1 — it is the app route POST /api/artifact/quick-share, which also accepts X-API-Key. Its fields are camelCase (allowJs, shareToken), not the snake_case used across /v1. See HTML Artifacts.

Personal Fonts

Upload OTF, TTF, or WOFF2 files to your font library, then pass family ids as font_ids on image creation, image editing, or image-mode Creative Director.

EndpointDescription
GET /v1/fontsList / search fonts (source=custom | global | all)
POST /v1/fontsUpload one font file (multipart field file)
GET /v1/fonts/uploads/:idPoll upload processing status
POST /v1/fonts/upload-ticketsMint a one-use upload ticket for remote or browser uploads
GET /v1/fonts/:idInspect a family, its styles and scripts
PATCH /v1/fonts/:idRename a family
DELETE /v1/fonts/:idDelete a family

See Personal Fonts.

Video Analysis, Video Editor and Downloads

EndpointDescription
POST /v1/analyze/videoAsk questions about a video by URL. Answers inline (no polling), token-billed. See Video Analysis
GET /v1/editor/schema, GET /v1/editor/sessions, GET /v1/editor/sessions/:session_id, POST /v1/editor/sessions, PATCH /v1/editor/sessions, POST /v1/editor/exportsBuild editable timelines from existing media and export them to MP4. Create and export are limited to 5/minute. See Video Editor API
POST /v1/downloads, GET /v1/downloads/:jobId, DELETE /v1/downloads/:jobIdDownload media from a public page URL into your library as a server job (start, poll, cancel). Start and cancel need a write-enabled key; start is limited to 5/minute

Other Endpoints

EndpointDescription
GET /v1/modelsList available models — the source of truth for identifiers, supported_* constraints, and credit multipliers. Query: type, provider
GET /v1/account/creditsCheck credit balance
GET /v1/account/plansRead-only plan and credit-pack catalog (purchases stay on app.kolbo.ai)
GET /v1/generate/:id/statusPoll generation status
POST /v1/generate/:id/cancelCancel a running generation and release/refund its reserved credits
GET /v1/generate/creative-director/:id/statusPoll Creative Director status (per-scene)
GET /v1/project/lightweightLightweight project list (legacy path — prefer GET /v1/projects)
POST /v1/generate/music/composition-planFree: turn a prompt into an editable structured-song plan for composition_plan (nothing is generated or charged)
POST /v1/generate/music/reference-audioBilled: register a media-library audio item (media_id) as a structured-song reference; returns a song_id
/v1/lipsync-plans/:sessionId/*Build, prepare, and quote performance plans for plan-driven lipsync models. See Elements: Performance plans
/v1/flows/*Read, edit, estimate, and run Flow Builder canvases. See Flow Builder
/v1/trends/*, /v1/recast-presets/*Run trend recipes, Motion Library presets, and catalog styles; browse the Motion Library. See Trends
/v1/morphious-styles/*Morphious look catalog and custom styles. See Morphious Styles
/v1/blender/*, /v1/adobe/*, /v1/resolve/*Bridges to connected Blender, Adobe (Premiere Pro / After Effects), and DaVinci Resolve sessions. See Blender, Adobe & Resolve Bridges

Reference

Authentication

All requests require the X-API-Key header with your API key:

X-API-Key: YOUR_API_KEY

See Authentication for details on creating and managing keys.

Claude Code Integration

Use Kolbo as native tools in Claude Code via our MCP server. See Claude Code Setup.