Kolbo.AIKolbo.AI Docs
Developer API

AI Docs (Magic Pad)

Create, edit, and share project-scoped documents — the same docs users see in the Kolbo app's Magic Pad editor.

AI Docs are HTML documents that live inside a project, editable in the Kolbo app's Magic Pad editor. Through the API you (or an LLM driving the MCP) author the document content directly — production plans, briefs, scripts, research summaries — and the user picks it up in the app, fully formatted and editable.

Content is HTML and is sanitized server-side against a tag allow-list before it is stored. Stick to semantic elements the editor understands: <h1><h6>, <p>, <br>, <ul>/<ol>/<li>, <table> (with <thead>/<tbody>/<tfoot>/<tr>/<th>/<td>/<colgroup>/<col>), <blockquote>, <code>/<pre>, <strong>/<b>, <em>/<i>, <u>, <s>/<strike>/<del>, <mark>, <sub>/<sup>, <small>, <a>, <img>, <hr>, <div>/<span>, <figure>/<figcaption>, <details>/<summary>, <label>.

What the sanitizer actually does: <script> and <style> tags, on* event handlers, and javascript: URLs are removed. Inline style attributes are kept for a fixed list of safe CSS properties (colors, alignment, font, spacing, borders, sizing). <iframe> is kept but only for a fixed embed allow-list (YouTube and youtube-nocookie, Vimeo, Spotify, SoundCloud, CodePen, CodeSandbox, Google Maps, Figma, Loom) over https. <input> survives only as a disabled checkbox, for task lists. Every <a> is rewritten with rel="noopener noreferrer", and its href may only be http, https, or mailto. <img src> additionally allows data: URIs, so small inline images survive. Protocol-relative (//host/…) URLs are stripped everywhere.

Endpoint

POST   /api/v1/docs
GET    /api/v1/docs
GET    /api/v1/docs/:id
PUT    /api/v1/docs/:id
PATCH  /api/v1/docs/:id/share
DELETE /api/v1/docs/:id

All six routes are limited to 120 requests per minute, keyed by authenticated user — several API keys on one account share the counter, and it is the same bucket as the media-library routes (see Errors & Limits).

On the four /docs/:id routes, a malformed id returns 400 with "Invalid doc id." rather than 404.

Create a Doc

POST /api/v1/docs

Request Body

FieldTypeRequiredDescription
titlestringYesDocument title. Must be a non-empty string; trimmed, then truncated to 200 characters rather than rejected.
contentstringNoFull HTML body of the document. Must be a string when present (400 otherwise). Omitted → the doc is created empty.
project_idstringNoProject to create the doc in — needs edit permission or higher (see Projects). An invalid id returns 400 SDK_PROJECT_INVALID_ID; one you cannot write to returns 403 SDK_PROJECT_ACCESS_DENIED or 404 SDK_PROJECT_NOT_FOUND. Omitted → your auto-created "API Generations" project, created on the spot if it does not exist yet.

Any other field in the body is ignored. Creating a doc also bumps the parent project's updated-at timestamp so it surfaces at the top of the project list — that bump is debounced to once per 30 seconds per project per server process, so a burst of creates only moves it once. Updating a doc does not bump it.

curl -X POST https://api.kolbo.ai/api/v1/docs \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Production Plan",
    "content": "<h1>Production Plan</h1><p>Shot list and schedule…</p>",
    "project_id": "65f1c8a2e4b0a3c1d9f5e123"
  }'

Response (201):

{
  "success": true,
  "doc": {
    "id": "6a53c31f49989427bdaf199b",
    "title": "Production Plan",
    "project_id": "65f1c8a2e4b0a3c1d9f5e123",
    "is_shared": false,
    "is_editable_by_link": false,
    "share_url": null,
    "created_at": "2026-07-12T16:38:55.871Z",
    "updated_at": "2026-07-12T16:38:55.871Z"
  }
}

List Docs

GET /api/v1/docs
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.
pagenumberNo1-indexed. Default 1.
limitnumberNoResults per page. Default 20, max 50.

Returns metadata only (no content), most recently updated first, with pagination: { page, limit, total, pages }.

Scope depends on project_id. With project_id, you get every member's docs in that shared project, and each row carries owner ({ userId, name, avatar }) plus is_mine. Without it, you get only your own docs across all projects — a teammate's doc will not appear.

Get a Doc

GET /api/v1/docs/:id

Returns the doc metadata plus its full HTML content, and always includes owner / is_mine. Readable by the owner and by any member of the doc's project. An id you cannot access returns 404 (existence is never leaked).

Update a Doc

PUT /api/v1/docs/:id
FieldTypeRequiredDescription
titlestringNo*New title. Must be a non-empty string; truncated to 200 characters.
contentstringNo*Full replacement HTML — replaces the whole document. Read first, apply edits, send the complete result.

* At least one of title / content must be present, otherwise 400. A title that is present but empty or non-string is 400; a non-string content is 400.

Writable by the owner and by project members with edit permission or higher. A doc you cannot write to returns 404, not 403 — existence is never leaked. Note the response is the metadata shape only; it does not echo content back.

Updating a doc does not move it between projects. There is no route for that — create the doc in the right project, or move it in the app.

Share a Doc

PATCH /api/v1/docs/:id/share
FieldTypeRequiredDescription
sharedbooleanYestrue = publicly shared, false = private (the link stops working). Explicit set, not a toggle — a non-boolean returns 400.
editablebooleanNoAllow link visitors to edit the doc. Omitted → unchanged (false on a new share).

Requires the same write access as PUT. The share token is generated once on the first share and reused afterwards, so re-sharing a doc gives back the same URL.

curl -X PATCH https://api.kolbo.ai/api/v1/docs/6a53c31f49989427bdaf199b/share \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "shared": true }'
{
  "success": true,
  "doc": {
    "id": "6a53c31f49989427bdaf199b",
    "title": "Production Plan",
    "project_id": "65f1c8a2e4b0a3c1d9f5e123",
    "is_shared": true,
    "is_editable_by_link": false,
    "share_url": "https://app.kolbo.ai/shared/magicpad/6d5f837497f742393cb766b0c59f728a",
    "created_at": "2026-07-12T16:38:55.871Z",
    "updated_at": "2026-07-12T17:04:02.118Z"
  }
}

share_url is only populated while is_shared is true. Setting shared: false returns share_url: null — the token is kept, so re-enabling restores the identical link.

Delete a Doc

DELETE /api/v1/docs/:id

Soft delete, owner only — project members with edit cannot delete a teammate's doc, and get 404. Deleting an already-deleted doc also returns 404.

{ "success": true, "doc": { "id": "6a53c31f49989427bdaf199b" } }

The response echoes only the id; there is no undelete route in the API.

Errors

StatusCause
400title missing or empty on create; neither title nor content on update; content not a string; shared not a boolean; malformed doc id or project_id.
403On create, project_id names a project you can only view (SDK_PROJECT_ACCESS_DENIED). On list, project_id names a project you are not a member of — the list route answers 403 here, while create answers 404.
404The doc does not exist, is soft-deleted, or you lack the required access — read needs any project membership, write needs edit or higher, delete needs ownership. Also returned by create when project_id is a project you cannot see at all (SDK_PROJECT_NOT_FOUND).

MCP Tools

The matching @kolbo/mcp tools are create_doc, list_docs, get_doc, update_doc, share_doc, and delete_doc. The intended flow for an LLM:

1. (user names a project?) list_projects -> capture id
2. create_doc { title, content: "<full HTML you authored>", project_id }
3. (user wants a link?) share_doc { doc_id, shared: true } -> give share_url to the user

create_doc makes content required, which is stricter than the HTTP route — over MCP you cannot create an empty doc. Its inline guidance also tells the model that <iframe> is stripped; in reality the sanitizer keeps allow-listed https embeds, so a YouTube or Figma embed you write will survive.