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

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). Besides the required name, 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 1730). You may hold at most 50 active keys; creating the 51st returns a 400. If you lose the key you can re-reveal it with GET /api/api-keys/:keyId/reveal (same JWT auth, rate limited) 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, video-to-video, chat, transcription, 3D). POST /v1/generate/music, /v1/generate/speech, and /v1/generate/sound instead substitute a concrete default identifier for that type and echo it; POST /v1/edit/image and POST /v1/edit/video echo null. poll_interval_hint is seconds: 2 for chat, 3 for image and image-edit, 5 for sound and Creative Director, 8 for everything else. 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, "base": 10, "final": 12 }
  ]
}

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). credits_used / credits_breakdown are the real multiplier-adjusted spend and are present only once state === "completed". On state === "failed" you get error (a string) plus a structured failure object with message, category, code, retryable, severity, and provider — 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 — 200 (image, 3D, Shorts analyze), 201 (/generate/image-edit), and 202 Accepted (/edit/image, /edit/video, speech, sound, lipsync, elements, first-last-frame, Creative Director, video trim) are all in use, 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) returns a 2xx with success: false, and errors from the global middleware 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 generation endpoint, message_id on POST /v1/chat, jobId on video trim, and data.jobId on Shorts Creator. 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. 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 { generation_id } = await res.json();

  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"}
    )
    generation_id = res.json()["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. Video Trim is the one exception — it 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). 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
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
Video TrimPOST /v1/video/trimVideo Editing

Rate limits differ by endpoint. Image endpoints (/v1/generate/image, /v1/generate/image-edit, /v1/edit/image) allow 30 requests/minute; /v1/transcribe allows 100/minute; every other generation endpoint defaults to 10/minute. See Errors & Limits.

Shorts Creator is HTTP-only — the @kolbo/mcp server does not register tools for it, so an MCP client has to call these endpoints directly with an X-API-Key header.

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
DELETE /v1/visual-dna/:idDelete a Visual DNA

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. Pass moodboard_id to image generation, image editing, or Creative Director requests. See Moodboards for details.

Color DNA

A saved color palette that becomes one project's grade. A project stores at most one palette (castColorPaletteId), and while it is set, generations that run inside that project are color-graded automatically. There is no account-wide palette — a generation with no resolved project gets no grade.

EndpointDescription
GET /v1/color-palettesList the user's palettes (personal + active-org scope). Query: page (default 1), limit (default 50, max 100), projectId. is_active means "is this project's grade" — without projectId every row comes back is_active: false
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), projectId (string). It only becomes a grade when projectId is supplied and is_active is not false — otherwise it is just saved to the library
PUT /v1/color-palettes/:idRename and/or replace colors / source_image_urls (owner only, never changes the grade). Any other body key is rejected with 400
DELETE /v1/color-palettes/:idDelete a palette and clear it from every project that pointed at it
POST /v1/color-palettes/:id/activateMake this palette the grade for one project. Body: projectId (required — omitting it returns 400 "Open a project to set its Color DNA"; a viewer-only member gets 403)
POST /v1/color-palettes/deactivateClear a project's grade. Body: projectId (required, same 400/403 rules)
POST /v1/color-palettes/analyzeExtract colors from 1–5 image URLs. Body: image_urls (array, required, 1–5). Local pixel analysis, free, saves nothing

projectId is camelCase on these routes (not project_id like the generation endpoints), and it is required on activate and deactivate.

skip_color_palette: true opts one request out of the project's grade without clearing 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 is the bare grouped map{ "<dimension>": [{ id, name, description, thumbnail_url, preview_url?, sort_order, bundle? }, …] } — not an envelope, and it is ETag-cached (send If-None-Match and expect 304). The dimension keys are data-driven (the catalog can serve looks, camera, lens, focal_length, aperture, angle, shot_type, lighting, color_palette, genre, 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. 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/audioDownloadable 128/320/WAV URLs
GET /v1/music-library/track/:id/relatedStems + alternate versions
GET /v1/music-library/track/:id/lyricsLyrics text + theme
POST /v1/music-library/clean/:trackIdPaid — acquire a clean, unwatermarked MP3/WAV
POST /v1/music-library/importPaid — acquire a clean file and copy it into the media library

The two acquisition endpoints spend a vendor credit on the first call, with no confirmation step, and require a write-enabled API key — a read-only key gets 403 API_KEY_READ_ONLY. Both are idempotent through the request id you supply, so send a stable one when retrying. They also share a much tighter rate limit than the free 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) and receive a stable URL
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, type, category (section), source_type, search, sort, include_metadata, pagination (page, page_size). 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 (atomic, ≤1000)
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. Optional type filter: image, video, music, text_to_video (alias of video), or shorts. Omit type and every catalog is returned in one flat presets[], each row tagged with its own type

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)
PATCH /v1/sessions/:sessionId/projectMove a session (any type) and all its media to another project
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
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, PUT /v1/projects/:id, PUT /v1/projects/:id/archive, PUT /v1/projects/:id/unarchiveProject lifecycle: create, rename, archive, unarchive (deletion stays in-app)
GET /v1/sessionsList sessions across all types (filter by project/type)
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

Custom Chat Agents

Reusable named personas for chat. The agent's description is the system instruction the model adopts.

EndpointDescription
GET /v1/agentsList your agents (personal + global presets)
POST /v1/agentsCreate an agent
PUT /v1/agents/:idUpdate an agent (personal agents only — global presets are protected)
DELETE /v1/agents/:idDelete an agent

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; 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 — returns 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.

Other Endpoints

EndpointDescription
GET /v1/modelsList available models — the source of truth for identifiers, supported_* constraints, and credit multipliers
GET /v1/account/creditsCheck credit balance
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)

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.