Kolbo.AIKolbo.AI Docs
Developer API

HTML Artifacts

Publish an HTML page, SVG, or Mermaid source to a public Kolbo URL and update it in place.

Publish a self-contained HTML page (or an SVG / Mermaid document) and get back a public, shareable URL. This is what the MCP tool publish_html_artifact uses for one-off deliverables: presentations, landing pages, dashboards, reports.

This endpoint is not under /api/v1. It is a Kolbo app route — POST /api/artifact/quick-share — that also accepts the X-API-Key header, so API-key clients can call it exactly like a /v1 route. There is no /api/v1 equivalent, and its request/response fields are camelCase (allowJs, shareToken), not the snake_case used across /v1.

Endpoint

POST /api/artifact/quick-share

Request Body

FieldTypeRequiredDescription
titlestringYesPage title, also the base of the SEO slug. Missing or empty → 400 title and content are required. Keep it short; the slug is truncated to 50 characters.
contentstringYesThe raw document. For html a full HTML document (DOCTYPE + html/head/body); for svg an <svg> document; for mermaid the Mermaid source text. Missing or empty → 400.
typestringNoDefault: "html". The MCP tool exposes "html" | "svg" | "mermaid"; the underlying record also accepts "react", "jsx", "tsx", "code". Any other value fails schema validation and returns 500.
allowJsbooleanNoDefault: false. Stored on the artifact record. It does not gate script execution on the published page — see the CSP callout.
shareTokenstringNoUpdate a previous publish in place. Lookup is scoped to (shareToken, your user); an unknown or foreign token returns 404 Artifact not found. Omit on first publish.
sessionIdstringNoOptional Mongo ObjectId of a chat session to associate. A non-ObjectId string fails validation (500).
messageIdstringNoOptional Mongo ObjectId of a chat message to associate. Same validation caveat.

Identical content is deduplicated. The server hashes the sanitized content and, on a first publish, returns the existing artifact when that hash already exists among your quick-share artifacts (the lookup is scoped to your user plus the container project described below, so an artifact you published from inside a normal Kolbo project does not collide). That response is 200 with duplicate: true instead of 201, and the URL is unchanged.

html content is sanitized before storage. <iframe>, <object>, <embed>, and <applet> are stripped outright. <script>, forms, canvas, SVG, and media elements survive. Inline event handlers survive only from a fixed allow-list — onclick, onchange, oninput, onsubmit, onload, onerror, onmouseover, onmouseout, onkeydown, onkeyup, onfocus, onblur — so anything else (pointer, drag, touch, wheel handlers) is removed; bind those with addEventListener inside a <script> instead. svg and mermaid content is stored verbatim, without sanitization.

The request body is capped at 1 MB. content travels as JSON, so a page whose markup plus inlined CSS/JS exceeds roughly a megabyte is rejected by the body parser with 413 before the handler runs. Load heavy libraries from the CDNs the CSP already allows instead of inlining them.

Examples

Publish a New Page

curl -X POST https://api.kolbo.ai/api/artifact/quick-share \
  -H "X-API-Key: kolbo_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Q3 Campaign Dashboard",
    "type": "html",
    "content": "<!DOCTYPE html><html><head><title>Q3</title></head><body><h1>Q3 Campaign</h1></body></html>",
    "allowJs": true
  }'

Response (201):

{
  "status": true,
  "data": {
    "_id": "65f1c8a2e4b0a3c1d9f5e321",
    "title": "Q3 Campaign Dashboard",
    "type": "html",
    "shareToken": "a3f4e2b1c8d90a1b2c3d4e5f60718293",
    "shareableSlug": "q3-campaign-dashboard-a3f4e2b1c8d90a1b2c3d4e5f60718293",
    "siteUrl": null,
    "isShared": true,
    "version": 1,
    "allowJs": true,
    "sharedAt": "2026-07-20T10:30:00Z"
  },
  "message": "Artifact created and shared successfully",
  "duplicate": false
}

data is the full artifact record plus two computed fields:

FieldNotes
shareToken32-char hex token. Keep it — it is how you update the page later.
shareableSlug<slugified-title>-<shareToken>. This is the path segment on the sites host.
siteUrlOnly non-null when a custom subdomain has been assigned to the artifact in the app and is marked active. Build the public URL from shareableSlug otherwise.
duplicatetrue when the content hash already existed and no new artifact was created.
updatedPresent on the update path only (true when a new version was written).

Update in Place

Pass the shareToken from a previous publish. The public URL stays the same and the previous content moves into version history.

curl -X POST https://api.kolbo.ai/api/artifact/quick-share \
  -H "X-API-Key: kolbo_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Q3 Campaign Dashboard",
    "content": "<!DOCTYPE html><html>...updated...</html>",
    "shareToken": "a3f4e2b1c8d90a1b2c3d4e5f60718293"
  }'

Response (200):

{
  "status": true,
  "data": { "version": 2, "shareToken": "a3f4e2b1c8d90a1b2c3d4e5f60718293", "...": "..." },
  "message": "Artifact updated in place",
  "duplicate": false,
  "updated": true
}

On the update path: title is applied only when it is a non-blank string, allowJs only when it is a real boolean, messageId only when truthy, and if the new content hashes identically to the current content nothing is written — you get 200 with duplicate: true, updated: false, and no version bump. sessionId is ignored on this path, and type is not written to the record — the artifact keeps the type it was created with. type does still decide how the new content is treated: omit it and the incoming content is sanitized as HTML, so always resend the same type you published with when updating an svg or mermaid artifact.

Reading the Published Page

Three public surfaces serve the same content. None require authentication.

URLReturns
https://sites.kolbo.ai/<shareableSlug>The page itself. The host resolves a custom subdomain first, then falls back to the share token embedded in the slug. Unknown slug → a branded 404 page.
https://api.kolbo.ai/api/shared-artifact-raw/<shareToken>Raw content as text/html.
https://api.kolbo.ai/api/shared-artifact/<shareToken>JSON metadata plus content (and the owner's public profile fields).

Both /api/shared-artifact* routes accept either the plain token or the full slug-token form. Both resolve the token with an isShared: true filter, so an unknown token and an artifact that has been un-shared in the app produce the same response: 404 with a JSON body — not an HTML page.

Rendered content is always served as text/html, whatever the artifact's type. Both surfaces that return the document itself — shared-artifact-raw and the sites host — send the stored content verbatim under Content-Type: text/html; charset=utf-8. (/api/shared-artifact/<shareToken> is the JSON surface and is unaffected.) An svg document still renders because browsers render inline SVG, but a mermaid artifact is served as its raw source text — the public URL does not render it into a diagram. Publish Mermaid as an html page that embeds the diagram if you need a viewable link.

The two /api/shared-artifact* routes are rate limited to 30 requests/minute per IP. POST /api/artifact/quick-share has no dedicated limiter of its own — only the global safety net of 5,000 requests/minute per API key.

Scripts always run on the published page; allowJs does not change that. Both surfaces that serve the document send the same fixed Content-Security-Policy, which allows script-src 'self' 'unsafe-inline' 'unsafe-eval' plus jsDelivr, cdnjs, unpkg, d3js.org, cdn.tailwindcss.com, and Cloudflare Insights. Also enforced: connect-src 'self' + Cloudflare Insights (no arbitrary fetch/XHR), form-action 'none' (no form submissions), object-src 'none', base-uri 'none', frame-ancestors 'self' https://*.kolbo.ai, images/media from any HTTPS origin, and frames only from YouTube and Vimeo — though html artifacts have their <iframe> tags stripped at save time anyway.

Slug lookups are cached for 60 seconds. A freshly updated page can serve the previous version to the sites host for up to a minute.

Where Artifacts Live

Quick-share artifacts are attached to a per-user container project named "Kolbo Code Artifacts", created on first publish and archived so it stays out of the normal project picker. There is no project_id parameter on this endpoint.

Errors

StatusCause
400title or content missing/empty.
401Missing or invalid API key.
403Key minted with scope enforcement and lacking the write (or admin) permission — code API_KEY_READ_ONLY. Also returned for a restricted or email-unverified account.
404shareToken supplied but no artifact with that token belongs to you.
413Request body over the 1 MB JSON limit.
500Invalid type, non-ObjectId sessionId/messageId, or a storage failure.

MCP Tool

publish_html_artifact wraps this endpoint:

MCP argSent as
titletitle (trimmed)
contentcontent
typetype"html" | "svg" | "mermaid", default "html"
allow_jsallowJs — always sent, as true only for an exact boolean true, otherwise false
share_tokenshareToken (trimmed), omitted when blank

The tool never sends sessionId or messageId; those are only reachable by calling the endpoint directly.

It returns { url, shareToken, shareableSlug, duplicate, updated, title }. Against production url is the artifact's siteUrl when a custom subdomain is active, otherwise https://sites.kolbo.ai/<shareableSlug>; on non-production hosts it falls back to the shared-artifact-raw URL.