Kolbo.AIKolbo.AI Docs
Developer API

Image Editing

Edit and transform images using AI models via the Kolbo API.

Kolbo exposes two different image-editing endpoints:

EndpointUse it for
POST /api/v1/generate/image-editPrompt-driven content edits — change the scene, add/remove/replace objects, restyle, recolor, composite multiple source images. Runs on dedicated editing models.
POST /api/v1/edit/imageTargeted operations — upscale, outpaint/expand, reframe, background removal or replacement, skin retouch, inpaint, erase, face swap, camera angle, grid split, multi-shot. Selected by an operation enum, not a prompt.

Both endpoints accept an optional project_id body field that routes the output into a specific project. See Projects.

Rate limit: the image endpoints (/v1/generate/image, /v1/generate/image-edit, /v1/edit/image) allow 30 requests/minute — higher than the 10/minute default on other generation endpoints.

Endpoint

POST /api/v1/generate/image-edit

Request Body

FieldTypeRequiredDescription
promptstringYesDescription of the edit to apply. Must be a non-empty string — anything else is a 400 INVALID_PROMPT.
modelstringNoModel identifier from GET /api/v1/models?type=image_edit (alias of type=image_editing). Must be a string — arrays/objects are a 400 INVALID_MODEL. Omit, or send any Smart Select alias ("", "auto", "auto-select", "smart-select", "smart_select", "smartselect", "kolbo_smart_select_router"), for Smart Select. You may also pass a text-to-image identifier — it is auto-routed to that model's editing variant (see below).
source_imagesarray of stringsConditionalURLs of the images to edit or composite. Required unless you pass visual_dna_ids — with neither, the request fails with 400 NO_FILE_UPLOADED. Cap: max_reference_images for the chosen model (GET /api/v1/models, falls back to 8). Extras beyond the cap are silently dropped, not rejected. Unlike reference_images on image generation, these are composited pixel-accurately. Refer to them in the prompt by ordinal ("FIRST source image") or @image1 / @image2 tags.
aspect_ratiostringNo"1:1", "16:9", "9:16", etc. Send a value from the model's supported_aspect_ratios on GET /api/v1/models. The value is not validated server-side — an unsupported ratio is forwarded to the provider and falls back to a supported one. Default: "1:1".
num_imagesnumberNoNumber of output images. Default: 1. Unlike image generation there is no clamp and no numeric coercion: only a falsy value (0, null, omitted) falls back to 1; anything else is passed straight through to the model. Models with a fixed images_per_request (GET /api/v1/models) return their own count regardless.
enhance_promptbooleanNoEnhance prompt for better results. Default: true (only an explicit false disables it).
visual_dna_idsarray of stringsNoVisual DNA IDs for character/style/product consistency. Cap: at most max_visual_dna IDs for the chosen model (GET /api/v1/models). The DNA's description is injected into the prompt as plaintext regardless of enhance_prompt. Do not pass a person's DNA when source_images already contains that person's face — the two references average together.
moodboard_idstringNoMoodboard ObjectId whose master prompt and style guide are applied. Must be a valid ObjectId — otherwise 400 Invalid moodboard_id format.
enable_web_searchbooleanNoGround the prompt with web search. Default: false.
cinematicobjectNoCinematic Presets — an optional "Cinema mode" object mapping dimensions (camera, lens, focal_length, aperture, angle, shot_type, color_palette, lighting) to preset ids from GET /api/v1/cinematic-presets. Include only the dimensions you want; omitted or null ones are Auto. Must be a plain object — arrays are ignored. Omit the object entirely for a non-cinematic edit.
skip_color_palettebooleanNoOpt this single call out of the account's active Color DNA palette. By default an active palette grades every edit. Only the exact value true skips it. Default: palette applied.
resolutionstringNoResolution tier, e.g. "1K", "2K", "3K", "4K". Model-dependent — send a value from the chosen model's supported_resolutions on GET /api/v1/models?type=image_edit. Higher tiers multiply the credit cost via resolution_multipliers — see Credit Multipliers. Omit to use the model default.
qualitystringNoQuality tier for models that have one — today the gpt-image-2 family ("low" | "medium" | "high" | "auto"). Read supported_qualities on GET /api/v1/models?type=image_edit; models with an empty list have no tier, so omit the field. "auto" is rewritten server-side to "medium" and bills at that tier. Multiplies credit cost via quality_multipliers — see Credit Multipliers.
preset_idstringNoSaved image-edit preset to apply. Discover IDs with GET /api/v1/presets?type=image_edit. This is a different collection from the presets accepted by image generation — an id from one is rejected by the other. Invalid or inactive ids are rejected.
project_idstringNoProject ObjectId to drop the edit into. Call GET /v1/projects to discover IDs. Omit to use the auto-created "API Generations" project.

You do not have to know the editing identifier. If you send a text-to-image model (e.g. gpt-image-2, nano-banana, flux-2), Kolbo automatically runs its editing variant (gpt-image-2/edit, nano-banana/edit, flux-2/edit). This happens before pricing, so the credits charged and the result.model returned on GET /v1/generate/{id}/status are those of the editing model.

Models with no editing variant (e.g. midjourney) and identifiers that are already editors are passed through unchanged. GET /api/v1/models exposes the link as editing_model_identifier — use it if you want to name the editor explicitly.

quality and preset_id are supported here. Earlier releases accepted both and dropped them at the API layer; they now reach the editor. quality selects the model's tier where it has one (today the gpt-image-2 family, which also accepts "auto") and affects cost — see Credit Multipliers. preset_id is validated against the image-edit preset collection, which is a different set from the presets used by image generation — an id from one is not valid on the other.

Insufficient credits fail synchronously. The credit gate runs before the edit is queued, so the initial POST returns 403 with "code": "INSUFFICIENT_CREDITS" rather than failing later during polling. The same gate protects POST /v1/edit/image.

Examples

Basic Edit (Smart Select)

curl -X POST https://api.kolbo.ai/api/v1/generate/image-edit \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Remove the background and place on a clean white surface",
    "source_images": ["https://example.com/product-photo.jpg"]
  }'

With a Specific Model

To choose a specific model, first fetch identifiers from GET /api/v1/models?type=image_edit, then pass the identifier value (e.g., nano-banana-2-image-editing, nano-banana-pro/edit):

curl -X POST https://api.kolbo.ai/api/v1/generate/image-edit \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Transform into a watercolor painting style",
    "source_images": ["https://example.com/photo.jpg"],
    "model": "nano-banana-2-image-editing",
    "enhance_prompt": false
  }'

JavaScript

const API_KEY = "YOUR_API_KEY";
const BASE_URL = "https://api.kolbo.ai/api"; // poll_url is relative to this

async function editImage() {
  const response = await fetch(`${BASE_URL}/v1/generate/image-edit`, {
    method: "POST",
    headers: {
      "X-API-Key": API_KEY,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      prompt: "Change the car color to red",
      source_images: ["https://example.com/car.jpg"],
      aspect_ratio: "16:9"
    })
  });

  const started = await response.json();
  if (!started.success) throw new Error(started.error);

  const result = await pollUntilDone(started.generation_id, started.poll_interval_hint);
  if (result.state !== "completed") throw new Error(result.error || result.state);

  console.log("Edited image URLs:", result.result.urls); // always an array
}

const TERMINAL = new Set(["completed", "failed", "cancelled"]);

// Minimal loop. A production client should also handle transient HTTP errors and
// an overall timeout — see /docs/developer-api/polling-and-cancellation.
async function pollUntilDone(generationId, intervalSeconds) {
  while (true) {
    await new Promise((r) => setTimeout(r, intervalSeconds * 1000));
    const status = await fetch(`${BASE_URL}/v1/generate/${generationId}/status`, {
      headers: { "X-API-Key": API_KEY }
    }).then((r) => r.json());
    if (TERMINAL.has(status.state)) return status;
  }
}

editImage();

Python

import requests
import time

API_KEY = "YOUR_API_KEY"
BASE_URL = "https://api.kolbo.ai/api"  # poll_url is relative to this

# Start edit (Smart Select picks the best model automatically)
response = requests.post(
    f"{BASE_URL}/v1/generate/image-edit",
    headers={"X-API-Key": API_KEY},
    json={
        "prompt": "Remove background and add soft shadow",
        "source_images": ["https://example.com/product.jpg"]
    }
)
started = response.json()

TERMINAL = {"completed", "failed", "cancelled"}

# Minimal loop. Add transient-error retries and an overall timeout for production
# — see /docs/developer-api/polling-and-cancellation.
while True:
    time.sleep(started["poll_interval_hint"])
    status = requests.get(
        f"{BASE_URL}/v1/generate/{started['generation_id']}/status",
        headers={"X-API-Key": API_KEY}
    ).json()
    if status["state"] in TERMINAL:
        break

if status["state"] != "completed":
    raise Exception(status.get("error", status["state"]))

print("URLs:", status["result"]["urls"])  # always a list

Response

POST /v1/generate/image-edit is asynchronous and fire-and-forget. It answers 201 Created with a job id as soon as the edit is queued — the response never contains an image.

Polling is the only completion mechanism. Kolbo never calls you back: there is no webhook, no callback_url field on this request body, and no route delivers outbound notifications. Socket.IO events are the web app's internal transport and are not part of the API contract — API-key generations are registered so the shared progress emitter drops their events, and the few emit sites that bypass that check are undocumented, unversioned and unsafe to build on. After the POST, loop against GET /api/v1/generate/{generation_id}/status until state is completed, failed or cancelled. Full contract: Polling & Cancellation.

Generation Started

{
  "success": true,
  "generation_id": "6601a1b2c3d4e5f6a7b8c9e2",
  "type": "image_edit",
  "model": "auto",
  "credits_charged": null,
  "poll_url": "/v1/generate/6601a1b2c3d4e5f6a7b8c9e2/status",
  "poll_interval_hint": 3,
  "session_id": "6601a1b2c3d4e5f6a7b8c9d0",
  "project_id": "6601a1b2c3d4e5f6a7b8c9d1"
}
FieldTypeNotes
generation_idstringMongo ObjectId. This is what you poll and cancel with.
typestringAlways "image_edit" for this endpoint
modelstringEchoes "auto" when Smart Select was used, otherwise the identifier you sent
credits_chargednullAlways null on this endpoint — the edit pipeline reports no estimate at submit time. Read credits_used from the completed status.
poll_urlstringThe status path without the /api prefix. Prepend https://api.kolbo.ai/api.
poll_interval_hintnumberSuggested seconds between polls — 3 for image edits
session_id / project_idstringWhere the edit lives in the Kolbo app

Completed Status

GET /api/v1/generate/{generation_id}/status

{
  "success": true,
  "generation_id": "6601a1b2c3d4e5f6a7b8c9e2",
  "type": "image_edit",
  "state": "completed",
  "progress": 100,
  "result": {
    "urls": ["https://media.kolbo.ai/kolbo/images/6601a1b2c3d4e5f6a7b8c9e2-0.png"],
    "thumbnail_url": "https://media.kolbo.ai/kolbo/images/6601a1b2c3d4e5f6a7b8c9e2-0.png",
    "prompt_used": "Enhanced version of your prompt",
    "model": "nano-banana-pro/edit",
    "created_at": "2026-03-05T10:30:00Z"
  },
  "credits_used": 18,
  "credits_breakdown": [
    { "model": "nano-banana-pro/edit", "amount": 18, "base": 9, "final": 18, "duration_multiplier": null, "pricing": null }
  ]
}

The output lives in result.urls — always an array of strings, even for a single edited image. result.thumbnail_url is literally urls[0], not a separate smaller asset.

result (and credits_used / credits_breakdown) appear only under state: "completed". A processing body has exactly five keys and no result at all. credits_used is the authoritative, multiplier-adjusted charge — not credits_charged. See Credits and Billing.

The completed result also echoes the creative inputs you sent, each key present only when it was used: visual_dna, moodboard, preset, cinematic_presets. model_name appears only when Smart Select recorded a display name.

result.model is the model that actually ran — never "auto". When Smart Select routed the edit, the status response resolves and returns the chosen identifier. The model on the initial POST response still reads "auto" because routing has not happened at that point.

Failure and cancellation

A failed edit is still an HTTP 200 with success: true — the failure is in state, with an error string and a best-effort failure object whose category / code are usually null on this pipeline. cancelled carries neither result nor error, so treat all three terminal states explicitly. Failed edits are not charged. See Polling & Cancellation.

Targeted Edits

POST /api/v1/edit/image

Applies one named operation to an existing image. Unlike /generate/image-edit, the behaviour is chosen by the operation enum rather than inferred from a prompt. Like the other generation endpoints, it returns immediately with a generation_id and is polled at /v1/generate/:generation_id/status.

Operations

operation is required. Sending anything outside this list returns a 400 listing the valid values.

OperationWhat it doesRequired fieldsOther fields it reads
upscaleIncrease resolution at a fixed . Routed to Topaz (model = topaz/upscale/image), Replicate (model = prunaai/p-image-upscale), or Clarity (any other value, and the default).model (scale and resolution are accepted but have no effect — see below)
clarity_upscaleAlias of upscale — same handler, same image_upscale model pool, same fixed 2×. Routing is decided only by model.model, prompt (used only on the Clarity route, as upscaler guidance)
reframeRe-generates the whole picture at a new aspect ratio. The subject is re-imagined, not preserved.aspect_ratiomodel
zoom_outOutpaint / expand / uncrop. Original pixels are preserved; only the new area is generated.zoom_out_percentage, expand_left, expand_right, expand_top, expand_bottom, prompt
removebgRemove the background (transparent output).model
background_replaceRemove the background and generate a replacement from prompt.promptmodel
enhance_skinPortrait skin retouching.skin_strength
inpaintRepaint a masked area. Needs a mask, but it is not checked at request time — see the callout below.mask_image_url, prompt, additional_images, model, resolution and quality (credit multipliers only)
eraseErase the object inside the masked area. Needs a mask, but it is not checked at request time.mask_image_url, additional_images, model, quality (forwarded as the object-removal tier; default best_quality)
face_swapSwap the face in image_url with the face reference.mask_image_url or a non-empty additional_imagesmodel
camera_angleRe-render the shot from a different camera angle.prompt, generate_all_angles
splitSplit an existing grid image into its tiles. The layout is auto-detected from the source; no generation model is called.
split_upscaleTopaz upscale at a hardcoded 4× with face enhancement, then split into tiles. Billed at a flat 40 credits regardless of model, so model, resolution, quality, and scale change nothing.
multi_shotGenerate a 3×3 camera-coverage grid from image_url and auto-split it. Returns 9 shots plus the full coverage grid as the last URL. The model, prompt, and framing are all pinned server-side.resolution (output size and credit multiplier), quality (credit multiplier only)
magic_editDeprecated. Prompt-driven content edit. The request is internally rewritten and forwarded to POST /v1/generate/image-edit with source_images = [image_url, ...additional_images] — so it is tracked as "type": "image_edit", not global_image_edit, and every other field on this endpoint is dropped.promptmodel (defaults to nano-banana-pro/edit), additional_images, project_id

zoom_out vs reframe. If the user wants to expand, extend, widen, uncrop, or add space while keeping the existing artwork, use zoom_out. reframe re-generates the entire image and produces a different-looking picture. aspect_ratio is ignored by zoom_out — size that expansion with zoom_out_percentage or the expand_* pixel fields.

Prefer /v1/generate/image-edit for open-ended content edits ("make it night", restyling, adding or removing objects). It runs on stronger dedicated editing models. magic_edit here exists only for backward compatibility and forwards to that endpoint anyway.

Request Body

FieldTypeRequiredDescription
image_urlstringYesURL of the primary source image. Missing → 400 image_url is required.
operationstringYesOne of upscale, clarity_upscale, reframe, zoom_out, removebg, background_replace, enhance_skin, inpaint, erase, face_swap, camera_angle, split, split_upscale, multi_shot, magic_edit.
modelstringNoModel identifier override. Must be a string — arrays/objects are a 400 INVALID_MODEL. Omit to use the platform default for the operation. Unlike the two /generate/* image endpoints, Smart Select aliases are not recognised here: "auto" is treated as a literal identifier, matches nothing, and the request silently falls back to the operation's default model. Omit the field instead. Ignored entirely by multi_shot.
promptstringNoText instruction. Required for background_replace and magic_edit. Used by inpaint, zoom_out (describes the new area only), and camera_angle. Must be a string when present.
aspect_ratiostringNoTarget aspect ratio, e.g. "16:9". Required for reframe. Ignored by zoom_out.
scalenumberNoIntended upscale factor for operation="upscale". Accepted by the endpoint but not consumed by any upscale service — see the caveat below.
resolutionstringNoOnly multi_shot uses it to size the output: "1K" | "2K" | "4K", and any other value is coerced to "4K" (also the default). On multi_shot and inpaint it also multiplies the credit cost via the model's resolution_multipliers (GET /api/v1/models). Every other operation ignores it — upscale, clarity_upscale, and split_upscale all use their own fixed factor.
qualitystringNoRead by three operations, and only for billing on two of them. On inpaint and multi_shot it multiplies the credit cost on top of resolution via the model's quality_multipliers (GET /api/v1/models) without changing the output — send a value from that model's supported_qualities. On erase it is forwarded as the object-removal model tier: "low_quality" | "medium_quality" | "high_quality" | "best_quality", default "best_quality". All other operations ignore it.
skin_strengthstringNo"subtle" | "realistic" | "pimple" | "freckle". Only used by enhance_skin. Default: "realistic".
mask_image_urlstringNoBlack & white mask URL (white = affected area) for inpaint and erase. For face_swap this is the face reference image. Placed first in the additional-images slot.
additional_imagesarray of stringsNoExtra reference image URLs. Truncated to 8 entries total, counting mask_image_url when present (it is placed at index 0). Used by inpaint and erase (index 0 is read as the mask when mask_image_url is absent), face_swap (index 0 accepted as the face reference), and magic_edit (extra composite sources). multi_shot ignores it.
generate_all_anglesbooleanNoFor camera_angle: generate the full set of 12 angles (4 rotations × 3 tilts) instead of one. Billed at 12× the per-image model cost. Default: false.
zoom_out_percentagenumberNoFor zoom_out: uniform expansion on all sides as a percentage, clamped to 0–90. Default: 20.
expand_leftnumberNoFor zoom_out: pixels of new content on the left, clamped to 0–700. Default: 0.
expand_rightnumberNoFor zoom_out: pixels of new content on the right, clamped to 0–700. Default: 0.
expand_topnumberNoFor zoom_out: pixels of new content on top, clamped to 0–700. Default: 0.
expand_bottomnumberNoFor zoom_out: pixels of new content on the bottom, clamped to 0–700. Default: 0.
ai_optimizebooleanNoLet Kolbo enhance your prompt before sending it to the model. Default: true; only an explicit false sends the prompt verbatim.
project_idstringNoProject ObjectId to drop the edit into.

scale does not change the upscale factor. The Clarity and Topaz services read upscale_factor and the Replicate service reads scale_factor; the API populates none of them from scale, so operation="upscale" and operation="clarity_upscale" always run at the service default of whatever you send. split_upscale is separately hardcoded to 4×. There is currently no request field that controls image upscale size.

inpaint and erase do not validate the mask up front. Unlike reframe, background_replace, and face_swap, a missing mask_image_url (and empty additional_images) is not rejected with a 400. The request is accepted, credits may be reserved, and the generation later finishes with state: "failed". Always send a mask.

multi_shot is fully pinned server-side. The model is forced to a fixed editing model — a model you send is ignored. resolution is clamped to "1K" / "2K" / "4K" (anything else becomes "4K") so the billing multiplier always matches what is generated. The coverage prompt, the 3×3 split, and the output aspect ratio (detected from the source image) are all decided server-side, so prompt, aspect_ratio, and additional_images have no effect. One call returns 10 URLs: the 9 split shots, then the full coverage grid as the last entry.

Duplicate requests inside 30 seconds are rejected. The guard keys on user + edit session + operation + image_url, so the same image_url and operation re-sent from the same API key and project_id within 30 seconds — before the first edit finishes — returns 409 with "code": "DUPLICATE_REQUEST" and an existingGenerationId you can poll instead. The URL is compared case-insensitively with the query string stripped, so cache-busting a signed URL does not get you past it.

Example — expand an image to a wider frame

curl -X POST https://api.kolbo.ai/api/v1/edit/image \
  -H "X-API-Key: kolbo_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "image_url": "https://media.kolbo.ai/images/source.jpg",
    "operation": "zoom_out",
    "expand_left": 320,
    "expand_right": 320,
    "prompt": "continue the beach and horizon into the new space"
  }'

Example — remove the background

curl -X POST https://api.kolbo.ai/api/v1/edit/image \
  -H "X-API-Key: kolbo_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "image_url": "https://media.kolbo.ai/images/product.jpg",
    "operation": "removebg"
  }'

Example — inpaint with a mask

curl -X POST https://api.kolbo.ai/api/v1/edit/image \
  -H "X-API-Key: kolbo_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "image_url": "https://media.kolbo.ai/images/room.jpg",
    "operation": "inpaint",
    "mask_image_url": "https://media.kolbo.ai/images/room-mask.png",
    "prompt": "a green velvet armchair"
  }'

Example — nine camera shots from one still

multi_shot needs nothing but the source image: the coverage prompt, framing, and 3×3 split are all decided server-side.

curl -X POST https://api.kolbo.ai/api/v1/edit/image \
  -H "X-API-Key: kolbo_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "image_url": "https://media.kolbo.ai/images/scene.jpg",
    "operation": "multi_shot",
    "resolution": "4K"
  }'

The completed status returns the nine shots in result.urls, followed by the full coverage grid as a tenth URL.

Response

Also fire-and-forget: this endpoint answers 202 Accepted with a job id and nothing else. Polling is the only way to get the image — see Polling & Cancellation.

Edit started:

{
  "success": true,
  "generation_id": "gen_1772100000000_k3f9q1x7d",
  "type": "global_image_edit",
  "model": null,
  "credits_charged": null,
  "poll_url": "/v1/generate/gen_1772100000000_k3f9q1x7d/status",
  "poll_interval_hint": 8,
  "session_id": "6601a1b2c3d4e5f6a7b8c9d0",
  "project_id": "6601a1b2c3d4e5f6a7b8c9d1"
}

Unlike the other generation endpoints, generation_id here is a gen_<timestamp>_<random> string rather than an ObjectId — the status route accepts both formats. model echoes exactly what you sent, so it is null when you let the platform choose. credits_charged is always null on this endpoint — the underlying edit pipeline does not report a cost at submit time. poll_url is relative to https://api.kolbo.ai/api, and poll_interval_hint is 8 seconds here rather than the 3 used by the two /generate/* image endpoints.

Completed statusGET /api/v1/generate/{generation_id}/status:

{
  "success": true,
  "generation_id": "gen_1772100000000_k3f9q1x7d",
  "type": "global_image_edit",
  "state": "completed",
  "progress": 100,
  "result": {
    "urls": ["https://media.kolbo.ai/kolbo/images/edited-0.png"],
    "thumbnail_url": "https://media.kolbo.ai/kolbo/images/edited-0.png",
    "prompt_used": "remove the background",
    "model": "MODEL_IDENTIFIER",
    "created_at": "2026-03-05T10:30:00Z"
  }
}

The output lives in result.urls — an array, and split, split_upscale and multi_shot return several entries in it. result.thumbnail_url is the first URL. The status body echoes "type": "global_image_edit", but the result object itself uses the same shape as /generate/image-edit. There is no edit_type field on the result — the operation you requested is not echoed back.

No settled cost is reported for /v1/edit/image. credits_charged is null on submit, and unlike the two /generate/* image endpoints the completed status does not carry credits_used / credits_breakdown — this pipeline files its credit ledger rows against an internal session document rather than the gen_... id you poll with. To measure the spend, read your balance from GET /api/v1/account/credits before and after. See Credits and Billing.

Poll /v1/edit/image generations one at a time. Status for this endpoint is resolved through the shared daily API edit session rather than by document id, so it returns the most recent /v1/edit/image result in that session. Two concurrent edits, or re-polling an older generation_id after starting a newer edit, can both hand back the newer image. Run targeted edits sequentially, or isolate them by sending a different project_id — sessions are bucketed per user, per day, per project.

Finding Models

Model identifiers are Kolbo-specific — always fetch them dynamically rather than hardcoding. type=image_edit is an alias for the underlying model type image_editing; both work.

curl "https://api.kolbo.ai/api/v1/models?type=image_edit" \
  -H "X-API-Key: YOUR_API_KEY"

Read supported_aspect_ratios, supported_resolutions, supported_qualities, max_reference_images, and max_visual_dna off each model to know exactly which fields it accepts.

Tips

  • On /generate/image-edit, omit model to let Smart Select pick an editing model, or send an image_editing identifier for a deterministic choice. On /edit/image there is no Smart Select — omit model to get the operation's default.
  • source_images accepts URLs — no file uploads needed. The API downloads and processes them.
  • source_images may only be omitted when visual_dna_ids is supplied; the DNA's reference images then stand in for the source.
  • Use moodboard_id to layer a style preset on top of any edit. See Moodboards. moodboard_id, visual_dna_ids, cinematic, and skip_color_palette exist only on /generate/image-edit/v1/edit/image reads none of them.
  • On /generate/image-edit, read the settled cost from credits_used on the completed status rather than credits_charged on the submit response. /v1/edit/image reports neither — diff your balance from GET /v1/account/credits instead. See Credits and Billing.