Kolbo.AIKolbo.AI Docs
Developer API

Agents & Sessions

Manage reusable custom chat agents (Skills), and enumerate, rename, and trash sessions across every generation type.

Two workspace inventories that sit alongside Projects:

  • Agents (Skills) — reusable, named personas for the chat tool. The product now calls them Skills; every route is served under both /api/v1/skills (current name) and /api/v1/agents (original path, kept working) with identical behavior. An agent's description is the system instruction the model adopts.
  • Sessions — the containers every generation lands in. One endpoint enumerates them across all 17 generation and chat types.

Endpoint

GET    /api/v1/skills            (alias: /api/v1/agents)
POST   /api/v1/skills            (alias: /api/v1/agents)
PUT    /api/v1/skills/:id        (alias: /api/v1/agents/:id)
DELETE /api/v1/skills/:id        (alias: /api/v1/agents/:id)

GET    /api/v1/sessions
PATCH  /api/v1/sessions/:sessionId
DELETE /api/v1/sessions/:sessionId
POST   /api/v1/sessions/:sessionId/restore
PATCH  /api/v1/sessions/:sessionId/project
POST   /api/v1/sessions/move
GET    /api/v1/sessions/:sessionId/generations
POST   /api/v1/sessions/:sessionId/generations/move
POST   /api/v1/sessions/:sessionId/split
POST   /api/v1/sessions/organize/undo/:operationId

The examples below use /api/v1/agents; /api/v1/skills behaves identically.

Listing Agents

GET /api/v1/agents
ParameterTypeRequiredDescription
searchstringNoCase-insensitive filter on the agent name. Regex-escaped server-side, so user-typed text is safe. Also accepted as query.
pagenumberNo1-indexed. Default 1.
limitnumberNoDefault 50, max 100.

Returns your personal agents, the global preset agents, and agents a member has explicitly shared to your active organization. Deleted agents are excluded. Rows are sorted newest-created first across all three groups.

The list is paginated: count is the number of rows on this page, and pagination ({ page, limit, total, has_more }) tells you whether to request the next page. List rows always return file_ids: [] because the list query loads only a file count, not the file ids; the create and update responses carry the real file_ids.

curl "https://api.kolbo.ai/api/v1/agents?search=director" \
  -H "X-API-Key: YOUR_API_KEY"

Response:

{
  "success": true,
  "agents": [
    {
      "id": "66a1c8a2e4b0a3c1d9f5e001",
      "name": "Creative Director",
      "description": "You are a senior creative director. Turn any brief into a structured shot list…",
      "emoji": "<emoji>",
      "thumbnail": null,
      "is_global": false,
      "file_ids": [],
      "created_at": "2026-07-12T16:38:55.871Z",
      "updated_at": "2026-07-12T16:38:55.871Z"
    }
  ],
  "count": 1,
  "pagination": { "page": 1, "limit": 50, "total": 1, "has_more": false }
}

emoji holds a single emoji character; description, emoji and thumbnail come back as null when unset.

is_global: true marks a protected system preset. Presets show up in the list so you can reference them, but they cannot be edited or deleted through this API.

Creating an Agent

POST /api/v1/agents
FieldTypeRequiredDescription
namestringNo*Agent name. Omitted or blank → generated from description. Trimmed server-side.
descriptionstringNo*The persona and instructions — this becomes the system prompt in every conversation that uses the agent. Trimmed; an empty result is stored as null.
emojistringNoEmoji avatar. Omitted or blank → auto-picked from the name and description (falls back to the robot-face emoji).
thumbnailstringNoThumbnail image URL.
file_idsarray of stringNoUp to 10 media file ObjectIds to attach as the agent's retrieval corpus. Each must be a valid ObjectId of a non-deleted file you own. Ignored unless it is a JSON array.

* At least one of name / description must be a non-empty string, otherwise 400.

Any other body field (for example an organization-sharing flag) is dropped by the API route, so an agent created here is always a personal agent.

The API route applies no length limit to name or description. The in-app editor validates them at 100 and 8,000 characters respectively, but that validation sits on the web app's own route, not this one. Stay inside those bounds if the user needs to keep editing the agent in the app.

curl -X POST https://api.kolbo.ai/api/v1/agents \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Creative Director",
    "description": "You are a senior creative director. Turn any brief into a structured shot list with camera, lens, and lighting notes."
  }'

The response is { "success": true, "agent": { … } } in the shape shown above. Retrieval indexing for any attached files is queued in the background, so a freshly created agent is usable immediately but its file knowledge lands a moment later.

Create runs a plan-limit check, but it currently allows an unlimited number of agents on every plan, so there is no per-plan agent cap today. A view-only workspace context returns 403 (You don't have permission to create chat agents in this context). Match on the error message, not a code: the SDK error envelope drops the underlying code and data fields on this route.

Agent create / update / delete are limited to 30 requests per minute — the same bucket the video, music, speech, chat, and upload endpoints draw from, so creating agents eats into your generation allowance (requests rejected with an error status do not consume it). Listing is limited to 120 per minute, shared with the whole media-library surface. Both counters are keyed by authenticated user, so several API keys on one account share them. Full bucket map: Errors & Limits.

Agents are created through the API but selected in the app. POST /v1/chat has no field for an agent id — to apply a persona from an API-driven conversation, pass system_prompt on the Chat request instead. Agents you create here are for reuse in the web app's chat tool.

file_ids are MediaFile ObjectIds, and the value is only applied when it is a JSON array — a bare string is silently ignored. More than 10 ids, or a value that is not an ObjectId, returns 400 (Invalid agent files.); an id that is not a live file you own returns 403 (Some files are unavailable.). Sending the array replaces the whole file list rather than appending.

Updating an Agent

PUT /api/v1/agents/:id
FieldTypeRequiredDescription
namestringNoNew name.
descriptionstringNoNew description — replaces the old one, and also replaces the persona instructions the chat tool uses (unless you send instructions).
instructionsstringNoPersona instructions only, when they should differ from description. Wins over description for the persona.
emojistringNoNew emoji avatar. Sending emoji without thumbnail also clears the existing thumbnail.
thumbnailstringNoNew thumbnail URL.
file_idsarray of stringNoReplacement list of up to 10 media file ObjectIds you own (same validation as create). Ignored unless it is a JSON array.

At least one of those six fields must be present, otherwise 400. Only your own personal agents can be updated: a missing id, a global preset, and an agent owned by someone else all return 404. Fields outside that list are dropped, so is_global, ownership, and organization scope cannot be reassigned through this route. If the agent is changed elsewhere (for example in the app) between the server's read and write, the update returns 409 (The skill changed. Reload it before saving your edits.) — re-send it.

The response is { "success": true, "agent": { … } }. Retrieval indexing is re-queued in the background after every successful update.

Deleting an Agent

DELETE /api/v1/agents/:id

Owner only. A missing id, a global preset, or someone else's agent returns 404.

{ "success": true, "deleted": true, "id": "66a1c8a2e4b0a3c1d9f5e001" }

The agent is soft-deleted (flagged deleted and hidden from every list) and its retrieval embeddings are removed. There is no restore route in the API, so treat it as final. Conversations that used the agent are untouched.

Listing Sessions

GET /api/v1/sessions

Enumerates sessions across all generation and chat types, newest activity first. Trashed (soft-deleted) sessions are excluded.

ParameterTypeRequiredDescription
project_idstringNoRestrict to one project. Must be a valid ObjectId (400 otherwise) and you must be a member (403 otherwise). Also accepted as projectId.
typestringNoOne of the session types below. An unknown value returns 400 listing the valid keys.
typesstring or arrayNoSeveral session types at once — comma-separated (types=image,video) or repeated (types=image&types=video). Additive with type; every key is validated the same way.
pagenumberNo1-indexed. Default 1.
limitnumberNoDefault 20, max 50.
curl "https://api.kolbo.ai/api/v1/sessions?project_id=65f1c8a2e4b0a3c1d9f5e123&type=image&limit=50" \
  -H "X-API-Key: YOUR_API_KEY"

Response:

{
  "success": true,
  "sessions": [
    {
      "session_id": "65f1c8a2e4b0a3c1d9f5e999",
      "name": "API Image Generation - 2026-07-12",
      "type": "image",
      "types": ["image"],
      "project_id": "65f1c8a2e4b0a3c1d9f5e123",
      "created_at": "2026-07-12T16:38:55.871Z",
      "updated_at": "2026-07-12T18:02:11.004Z",
      "owner": { "userId": "65f1c8a2e4b0a3c1d9f5e001", "name": "Dana", "avatar": null },
      "is_mine": true
    }
  ],
  "pagination": { "page": 1, "limit": 50, "returned": 1, "has_more": false }
}

Scope depends on project_id. With project_id, you get every member's sessions in that shared project, and each row is stamped with owner ({ userId, name, avatar }) plus is_mine. Without it, you get only your own sessions across all projects, and rows carry neither field.

Several session types share one underlying store, so a row's type can come back as a pipe-joined string of every requested type backed by that store — with no filter, video|video_from_image|elements|first_last_frame, image|image_edit, or video_from_video|global_video_edit. The same keys arrive as an array in types, so prefer that over splitting the string. Passing a single type returns that one key. pagination reports returned and has_more rather than a total count.

Session types

The same 16 keys are valid for the type / types filters here and for the type hint on the rename, delete, restore, and move endpoints:

image, video, video_from_image, music, speech, image_edit, sound, creative_director, chat, elements, first_last_frame, lipsync, video_from_video, transcription, global_image_edit, global_video_edit

3D generations are project-scoped and have no session, so they never appear in this list.

A session's name comes back as null when it was never named, and project_id as null in the rare case a session is not attached to a project.

Renaming, Trashing, and Restoring a Session

PATCH  /api/v1/sessions/:sessionId           body: { name, type? }
DELETE /api/v1/sessions/:sessionId           query: type?
POST   /api/v1/sessions/:sessionId/restore   body: { type? }
FieldTypeRequiredDescription
namestringYes (rename only)New sidebar title. Trimmed; must be 1–200 characters, otherwise 400.
typestringNoSession type hint to speed up the lookup (one of the session types; unknown → 400). Accepted in the body or the query string on all three routes. Omit if unsure — every store is probed.
  • Rename needs edit permission on the session's project (the session owner always qualifies); otherwise 403.
  • Delete is a soft delete: the session is moved to the trash (deletedAt is set) and disappears from GET /sessions. Its generations and media are not touched. Needs delete permission (owner always qualifies).
  • Restore brings a trashed session back. It needs the same delete permission, and only finds sessions that are currently trashed.

An invalid id returns 400; a session that does not exist, is not attached to a project, or sits in a project you cannot access returns 404.

curl -X PATCH https://api.kolbo.ai/api/v1/sessions/65f1c8a2e4b0a3c1d9f5e999 \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Scene 03 — rooftop chase" }'

Each route returns { "success": true, "session": { session_id, name, type, types, project_id } }; delete adds deleted: true and deleted_at, and restore adds restored: true. All three sit on the 120-requests-per-minute media-library limiter.

Moving a Session

PATCH /api/v1/sessions/:sessionId/project relocates a session with all of its generations and media to another project. To move many at once use POST /api/v1/sessions/move, and to reorganize generations between sessions use the generations / split routes. Full reference: Projects → Moving a Session Between Projects, Moving Many Sessions at Once, Reorganizing Work Between Sessions and Undoing a Reorganization.

The move, split, generation-move, and undo routes share a stricter 20 requests per minute limiter; listing a session's generations uses the 120/min media-library limiter.

MCP Tools

The matching @kolbo/mcp tools are list_skills, create_skill, update_skill, delete_skill (also registered under their original names list_agents, create_agent, update_agent, delete_agent, which take agent_id where the new ones take skill_id), plus list_sessions, rename_session, delete_session, restore_session, move_session, bulk_move_sessions, list_session_generations, move_generations_to_session, split_session, and undo_session_organization.

1. list_skills { search: "director" }   -> resolve a name to a skill id
2. list_sessions { project_id }         -> find work that landed in the wrong place
3. move_session  { session_id, project_id }
4. delete_session { session_id }        -> trash the empty leftover, if any

The MCP create_skill / update_skill tools (and their *_agent aliases) do not expose file_ids — attach a retrieval corpus through the HTTP API or the web app. create_skill also makes description required (and name optional), which is stricter than the HTTP route, where either one alone is enough. list_skills exposes only search, so it returns the first page (up to 50 rows).

get_session_usage is not about these sessions. Despite the name, it does not read anything from list_sessions — it aggregates credit spend tagged with the X-Kolbo-Caller-Session-Id header (a per-app-launch id) and returns a total plus per-tool and per-model breakdowns. Only Kobi Code, which spawns the MCP with that id in its environment, sets it; on any other host the tool takes no arguments and answers locally that per-session usage is unavailable, pointing you to check_credits. It is served by the app API (GET /api/credit-usage/by-caller-session), not by any /api/v1 SDK route. For a plain balance, use GET /api/v1/account/credits — see Credits & Billing.