Color Palettes
Color DNA — save a palette and strict-grade every generation in a project to those colors.
A Color DNA palette is a named set of 1-10 colors. Activating a palette turns it into a strict color grade for a project: a plain-language directive ("dominant terracotta, secondary deep navy, accent warm gold…") is woven into the prompt of every graded generation in that project, telling the model to grade lighting, atmosphere, set dressing and wardrobe to those colors while leaving the subject and action alone. See the table below for exactly which endpoints are graded.
A grade belongs to a project, not to your account. There is no account-level default —
activating a palette requires a projectId, and a generation is only graded when it lands in
a project that has one active. SDK generations land in the auto-created "API Generations"
project unless you pass project_id, so that is usually the project you need to activate on.
Get its id from GET /api/v1/projects (the entry with is_default: true).
Where the grade is applied
| Endpoint | Graded | Per-request opt-out |
|---|---|---|
POST /api/v1/generate/image | Yes | skip_color_palette: true |
POST /api/v1/generate/image-edit | Yes | skip_color_palette: true |
POST /api/v1/generate/video (text-to-video) | Yes | skip_color_palette: true |
POST /api/v1/generate/creative-director | Yes (all scenes) | none — the endpoint does not accept skip_color_palette |
POST /api/v1/generate/video/from-image | No | skip_color_palette is accepted but has no effect |
skip_color_palette is a boolean, optional, default false (grade applied). It only affects the
single request it is sent on. Only the JSON literal true opts out — the string "true" is
mapped to false by the SDK layer.
Image-to-video is not graded. Grading is mode-gated to text-to-video — a source image
already fixes the colors, so grading the prompt would promise a look the output never takes.
POST /generate/video/from-image therefore ignores the project's palette, and
skip_color_palette there is a no-op.
Every other generation endpoint (Elements, first/last frame, video-to-video, lipsync, 3D,
speech, sound, music) is ungraded and does not accept skip_color_palette.
The directive is composed from each color's role and name in words only — hex codes are
deliberately never sent to the model, because image models render literal hex strings and color
strips as visible artifacts. A color saved without a name gets the nearest name from a built-in
color table.
Endpoints
GET /api/v1/color-palettes
POST /api/v1/color-palettes
POST /api/v1/color-palettes/analyze
PUT /api/v1/color-palettes/:id
DELETE /api/v1/color-palettes/:id
POST /api/v1/color-palettes/:id/activate
POST /api/v1/color-palettes/deactivateWhere a project id is accepted it is spelled projectId (camelCase), unlike the generation
endpoints which take project_id. Only four routes accept it at all: list (query string),
create, activate and deactivate (body). Update, delete and analyze have no project parameter —
and because the create/update bodies are validated strictly, sending projectId (or any other
unlisted field, including project_id) to PUT /color-palettes/:id is a 400 Validation error.
Error envelope. Successful responses use the normal SDK shape ({"success": true, …}), and
so do errors raised inside the handler ({"success": false, "error": "…"}). But the
create/update body validator and the shared rate limiter run before the handler, so their
rejections come back in the underlying app shape instead —
{"status": false, "message": "Validation error", "details": [...]} for a bad body, and
{"status": false, "error": "Too many generation requests", "message": "…", "retryAfter": 60}
for the 120/min bucket. Branch on the HTTP status code, not on the presence of a success
field, when handling errors from these seven routes.
List Palettes
Returns your personal palettes plus the palettes shared through your active organization,
newest first. When projectId is supplied, the palette currently grading that project is
included even if a teammate owns it, and is_active marks it.
Endpoint
GET /api/v1/color-palettesQuery Parameters
| Parameter | Type | Required | Constraint |
|---|---|---|---|
projectId | string | No | Project id. Determines which palette (if any) comes back with is_active: true. Without it — or with an id you cannot access — every palette is reported is_active: false. An unreadable id is ignored, not an error. |
page | integer | No | Default 1. Clamped to a minimum of 1. |
limit | integer | No | Default 50. Clamped to 1-100; a larger value is silently capped, not rejected. |
Example
curl "https://api.kolbo.ai/api/v1/color-palettes?projectId=65f1c8a2e4b0a3c1d9f5e789" \
-H "X-API-Key: kolbo_live_..."Response
{
"success": true,
"color_palettes": [
{
"id": "66a2b3c4d5e6f7a8b9c0d1e2",
"name": "Terracotta & Deep Navy",
"colors": [
{ "hex": "#E2725B", "name": "terracotta", "role": "dominant" },
{ "hex": "#0F1E3D", "name": "deep navy", "role": "secondary" },
{ "hex": "#E8C170", "name": "warm gold", "role": "accent" }
],
"source_image_urls": ["https://cdn.kolbo.ai/uploads/ref1.jpg"],
"is_active": true,
"content_scope": "personal",
"created_at": "2026-06-02T10:11:12.000Z",
"updated_at": "2026-06-02T10:11:12.000Z"
}
],
"pagination": { "page": 1, "limit": 50, "total": 3, "pages": 1 }
}content_scope is "personal" or "organization". Palettes created through this API are always
"personal". Organization palettes come from the in-app org admin surface and are stored with no
owner, so they are list-only here — update, delete and activate all match on ownership and
return 404 for them. To grade a project with an org palette's colors, copy its colors into a
new palette of your own.
Analyze Images
Extracts a palette from reference images with deterministic pixel math (median-cut
quantization) — no model call, no credits, and nothing is saved. Feed the returned colors
straight into the create endpoint.
Endpoint
POST /api/v1/color-palettes/analyzeRequest Body
| Parameter | Type | Required | Constraint |
|---|---|---|---|
image_urls | string[] | Yes | 1-5 publicly reachable image URLs. Private/internal addresses are rejected, redirects are not followed, and each image is capped at 20 MB. |
Images that cannot be downloaded are skipped; if none of them can be downloaded the request
returns 400. The response contains up to 6 colors, ordered by pixel population, with role
assigned as dominant (first), secondary (second), then accent. Near-duplicate colors are
merged, so you can get back fewer than 6. Each color's name is the nearest entry in a built-in
color-name table, and the palette name is built from the first two color names.
Unlike create/update, this body is not strictly validated — any field other than image_urls
is silently discarded. If image_urls is missing or out of range the error message names the
internal field: "imageUrls must be an array of 1-5 URLs". Send image_urls.
Example
curl -X POST https://api.kolbo.ai/api/v1/color-palettes/analyze \
-H "X-API-Key: kolbo_live_..." \
-H "Content-Type: application/json" \
-d '{
"image_urls": [
"https://cdn.kolbo.ai/uploads/moodboard-1.jpg",
"https://cdn.kolbo.ai/uploads/moodboard-2.jpg"
]
}'Response
{
"success": true,
"name": "Terracotta & Deep Navy",
"colors": [
{ "hex": "#E2725B", "name": "terracotta", "role": "dominant" },
{ "hex": "#0F1E3D", "name": "deep navy", "role": "secondary" },
{ "hex": "#E8C170", "name": "warm gold", "role": "accent" }
]
}Create Palette
Endpoint
POST /api/v1/color-palettesRequest Body
| Parameter | Type | Required | Constraint |
|---|---|---|---|
name | string | Yes | 1-100 characters after trimming. |
colors | object[] | Yes | 1-10 entries. See the color object below. |
source_image_urls | string[] | No | Max 5 entries, each a valid URI. Stored as provenance only. A 6th entry is a 400. |
is_active | boolean | No | Default true. Only has an effect when projectId is also sent; false suppresses activation. |
projectId | string | No | Project the new palette should immediately grade. Checked only when the palette is actually being activated (i.e. is_active is not false) — then you must be the project owner or a member shared with edit or full permission, otherwise 403, as is an id you cannot access or that does not exist. With is_active: false this field is ignored entirely and no access check runs. |
Color object:
| Field | Type | Required | Constraint |
|---|---|---|---|
hex | string | Yes | #RRGGBB, exactly 6 hex digits. |
name | string | No | Up to 60 characters after trimming; the empty string is accepted. Used verbatim in the prompt directive; when omitted or empty, the nearest built-in color name is used instead. |
role | string | No | dominant | secondary | accent | background. Stored default is accent, so responses always carry a role even when you omit it. |
is_active: true without projectId does not activate anything — the palette is saved to
your library and the response comes back with is_active: false. Send projectId, or call the
activate endpoint afterwards.
Example
curl -X POST https://api.kolbo.ai/api/v1/color-palettes \
-H "X-API-Key: kolbo_live_..." \
-H "Content-Type: application/json" \
-d '{
"name": "Terracotta & Deep Navy",
"colors": [
{ "hex": "#E2725B", "name": "terracotta", "role": "dominant" },
{ "hex": "#0F1E3D", "name": "deep navy", "role": "secondary" },
{ "hex": "#E8C170", "name": "warm gold", "role": "accent" }
],
"projectId": "65f1c8a2e4b0a3c1d9f5e789"
}'Response
{
"success": true,
"color_palette": {
"id": "66a2b3c4d5e6f7a8b9c0d1e2",
"name": "Terracotta & Deep Navy",
"colors": [
{ "hex": "#E2725B", "name": "terracotta", "role": "dominant" },
{ "hex": "#0F1E3D", "name": "deep navy", "role": "secondary" },
{ "hex": "#E8C170", "name": "warm gold", "role": "accent" }
],
"source_image_urls": [],
"is_active": true,
"content_scope": "personal",
"created_at": "2026-06-02T10:11:12.000Z",
"updated_at": "2026-06-02T10:11:12.000Z"
}
}Update Palette
Renames a palette and/or replaces its colors. Only the owner of the palette can update it —
anyone else gets 404. Activation is not affected.
Endpoint
PUT /api/v1/color-palettes/:idRequest Body
| Parameter | Type | Required | Constraint |
|---|---|---|---|
name | string | No | 1-100 characters after trimming. |
colors | object[] | No | 1-10 entries, same object shape as create. Full replacement — omit to keep the current colors. |
source_image_urls | string[] | No | Max 5 entries, each a valid URI. Replaces the stored list. |
Example
curl -X PUT https://api.kolbo.ai/api/v1/color-palettes/66a2b3c4d5e6f7a8b9c0d1e2 \
-H "X-API-Key: kolbo_live_..." \
-H "Content-Type: application/json" \
-d '{ "name": "Terracotta & Navy v2" }'Response
{
"success": true,
"color_palette": {
"id": "66a2b3c4d5e6f7a8b9c0d1e2",
"name": "Terracotta & Navy v2",
"colors": [{ "hex": "#E2725B", "name": "terracotta", "role": "dominant" }],
"source_image_urls": [],
"is_active": false,
"content_scope": "personal",
"created_at": "2026-06-02T10:11:12.000Z",
"updated_at": "2026-06-02T12:30:00.000Z"
}
}The is_active in this particular response is read from a legacy stored field and is always
false for palettes created through the API — it is not project-relative. To see which
palette is grading a project, call GET /api/v1/color-palettes?projectId=….
Delete Palette
Owner only (404 otherwise). Every project that was grading with this palette has its grade
cleared, so those projects go back to generating ungraded.
Endpoint
DELETE /api/v1/color-palettes/:idExample
curl -X DELETE https://api.kolbo.ai/api/v1/color-palettes/66a2b3c4d5e6f7a8b9c0d1e2 \
-H "X-API-Key: kolbo_live_..."Response
{ "success": true, "id": "66a2b3c4d5e6f7a8b9c0d1e2" }Activate Palette
Makes this palette the grade for one project, replacing whatever that project was using.
Endpoint
POST /api/v1/color-palettes/:id/activateRequest Body
| Parameter | Type | Required | Constraint |
|---|---|---|---|
projectId | string | Yes | Missing → 400 "Open a project to set its Color DNA". You must be the project owner or a member shared with edit or full permission, otherwise 403 — as is an id you cannot access or that does not exist. |
The palette must belong to you or to another member of that project — a palette owned by
someone outside the project returns 404.
Example
curl -X POST https://api.kolbo.ai/api/v1/color-palettes/66a2b3c4d5e6f7a8b9c0d1e2/activate \
-H "X-API-Key: kolbo_live_..." \
-H "Content-Type: application/json" \
-d '{ "projectId": "65f1c8a2e4b0a3c1d9f5e789" }'Response
{
"success": true,
"color_palette": {
"id": "66a2b3c4d5e6f7a8b9c0d1e2",
"name": "Terracotta & Deep Navy",
"colors": [{ "hex": "#E2725B", "name": "terracotta", "role": "dominant" }],
"source_image_urls": [],
"is_active": true,
"content_scope": "personal",
"created_at": "2026-06-02T10:11:12.000Z",
"updated_at": "2026-06-02T10:11:12.000Z"
}
}Resolved palettes are cached in-process for 60 seconds, keyed by user and project, on each API worker. An activate, deactivate or delete can therefore take up to a minute to change what a generation is graded with — for you as well as for teammates in the same shared project.
Deactivate Palette
Clears a project's grade. Generations in that project stop being color-graded. The palette itself is untouched and stays in your library.
Endpoint
POST /api/v1/color-palettes/deactivateRequest Body
| Parameter | Type | Required | Constraint |
|---|---|---|---|
projectId | string | Yes | Missing → 400 "Open a project to set its Color DNA". You must be the project owner or a member shared with edit or full permission, otherwise 403 — as is an id you cannot access or that does not exist. |
Example
curl -X POST https://api.kolbo.ai/api/v1/color-palettes/deactivate \
-H "X-API-Key: kolbo_live_..." \
-H "Content-Type: application/json" \
-d '{ "projectId": "65f1c8a2e4b0a3c1d9f5e789" }'Response
{ "success": true }Examples
Full flow: references → palette → graded generation
API_KEY="kolbo_live_..."
BASE="https://api.kolbo.ai/api/v1"
PROJECT="65f1c8a2e4b0a3c1d9f5e789" # from GET /api/v1/projects
# 1. Extract colors from reference images (free, saves nothing)
curl -s -X POST "$BASE/color-palettes/analyze" \
-H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
-d '{ "image_urls": ["https://cdn.kolbo.ai/uploads/ref1.jpg"] }'
# 2. Save the palette and grade the project with it in one call
curl -s -X POST "$BASE/color-palettes" \
-H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
-d "{
\"name\": \"Terracotta & Deep Navy\",
\"colors\": [
{ \"hex\": \"#E2725B\", \"name\": \"terracotta\", \"role\": \"dominant\" },
{ \"hex\": \"#0F1E3D\", \"name\": \"deep navy\", \"role\": \"secondary\" }
],
\"projectId\": \"$PROJECT\"
}"
# 3. Every generation in that project is now graded
curl -s -X POST "$BASE/generate/image" \
-H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
-d "{ \"prompt\": \"A coffee cup on a wooden table\", \"project_id\": \"$PROJECT\" }"
# 4. …unless you opt this one call out
curl -s -X POST "$BASE/generate/image" \
-H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
-d "{ \"prompt\": \"Plain product cutout\", \"project_id\": \"$PROJECT\", \"skip_color_palette\": true }"Stop grading a project
curl -X POST https://api.kolbo.ai/api/v1/color-palettes/deactivate \
-H "X-API-Key: kolbo_live_..." \
-H "Content-Type: application/json" \
-d '{ "projectId": "65f1c8a2e4b0a3c1d9f5e789" }'Credits and Rate Limits
- All Color Palette endpoints are free — no credits are charged, including
analyze. - Shared bucket: 120 requests per minute per user across the SDK's media/asset endpoints.
POST /color-palettes/analyzehas an additional dedicated limit of 10 requests per minute per user; exceeding it returns429with codeRATE_LIMITED.
Related
Image Generation
Generate images; opt out per call with skip_color_palette
Image Editing
Edits are graded by the project's active palette too
Video Generation
Text-to-video is graded; image-to-video is not
Projects
Find the project id to activate a palette on
Moodboards
Style templates — combine with a palette for color plus aesthetic
Cinematic Presets
Camera, lens, and lighting treatment on top of the grade