myDoc24/a/api-docs

myDoc24 Knowledge Base — API docs

Published 2026-05-20

On this page · 10 sections

myDoc24 Knowledge Base — API docs

Programmatic surface for reading articles, publishing content from scripts or AI agents, and embedding KB content into other applications.

All endpoints live under /api/kb/. CORS is Access-Control-Allow-Origin: * on read endpoints + widget.css + events so embedded widgets can fetch cross-origin.

For the human-facing side of the product, see the user guide.


Authentication

Every /api/kb/* request needs auth. There are four flows; pick the one that matches the caller:

Flow Header Use when
Personal access token Authorization: Bearer kbpat_<32 base62> Ad-hoc agent / script run by a signed-in user. Mint at /me/tokens.
Keycloak access token Authorization: Bearer <JWT> Authenticated user driving the call from their browser session.
Service-account client Authorization: Bearer <JWT> Unattended automation. KC client-credentials grant.
Guest API key X-Kb-Guest-Key: <secret> Server-to-server reads from a paired host app. Read-only.
Anonymous (no auth) Public-read deployments only — KB_PUBLIC_READ_MODE=true. Read-only.

Personal access tokens

Minted at /me/tokens. Scope is your current permissions, snapshotted at issuance — a read-only user gets a read-only token. The raw kbpat_… is shown once; the DB stores only the SHA-256 hash. Revoke from the same page; revoked tokens 401 immediately.

Service-account flow (unattended automation)

The kb-import-agent client exists in the realm config. Get its secret from the KC admin console → assign realm role kb:write:pages to its service account → mint tokens with:

TOKEN=$(curl -sS -X POST \
  -d "grant_type=client_credentials" \
  -d "client_id=kb-import-agent" \
  -d "client_secret=$KB_IMPORT_AGENT_SECRET" \
  https://<kc-host>/realms/<realm>/protocol/openid-connect/token \
  | jq -r .access_token)

Tokens last 5 minutes. Re-mint on demand — there's no refresh-token flow for client-credentials grants.

Guest API key

Shared secret in KB_GUEST_API_KEY, validated with crypto.timingSafeEqual. Materialises a synthetic principal { sub: "kb-guest", roles: ["kb:read:pages","kb:read:assets"], groups: [] }read-only, rejected for any write/manage permission.

Distribute the key out-of-band to the paired host application. Rotate from /admin/settings → Application → Guest API key. DB-backed, 30-second cache, no restart.


Read endpoints

All require kb:read:pages (or kb:read:assets for blobs), unless KB_PUBLIC_READ_MODE=true is set.

Endpoint Returns
GET /api/kb/folders Published tree of { id, name, slug, folders, articles }
GET /api/kb/article?slug=… Single article body — only when status is PUBLISHED
GET /api/kb/search?q=… FTS hits, ranked, max 50
GET /api/kb/blobs/[id] Binary stream with Cache-Control: public, max-age=31536000, immutable
GET /api/kb/events SSE channel — pushes tree-changed on every publish / archive / folder mutation. Pass token as ?token=… (EventSource can't set headers)
GET /api/kb/widget.css Stylesheet for embedded widgets. Always public, no auth

Example:

curl -sS -H "Authorization: Bearer kbpat_…" \
  "https://kb.example.com/api/kb/search?q=invoicing" | jq '.'

Write endpoint — import a Markdown article

POST /api/kb/admin/articles/import

Designed for AI agents authoring Markdown and having the server convert it to ProseMirror JSON + sanitised HTML.

Request:

curl -sS -X POST https://kb.example.com/api/kb/admin/articles/import \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title":      "How invoicing works",
    "slug":       "how-invoicing-works",
    "folderSlug": "getting-started",
    "keywords":   "invoice, billing, customer",
    "markdown":   "# Heading\n\nBody text…",
    "publish":    true
  }'

Response:

{ "id": 17, "slug": "how-invoicing-works", "status": "PUBLISHED" }

Behaviour:

  • Upsert by slug. If slug matches an existing article, that row is updated in place. Otherwise a new article is created and the slug is derived from the title (with -2, -3 collision suffixes).
  • publish: true runs the publish flow: status → PUBLISHED, paths / + /a/[slug] revalidated, draft row dropped, SSE tree-changed broadcast.
  • publish: false (default) writes to kb_article_draft — status stays DRAFT (or whatever it was) and the body is visible in the admin editor for human follow-up.
  • Markdown coverage: headings, paragraphs, lists, blockquotes, fenced + inline code, bold / italic / links, hr, hard-breaks, plus GFM-style callout blockquotes (> [!NOTE|TIP|WARNING|DANGER]<div class="kb-callout kb-callout-{variant}">). Tables, video, embed, and image-with-align remain editor-only.
  • Folder must exist and not be soft-deleted, otherwise 400.
  • created_by / published_by / last_saved_by get the caller's sub claim — real KC user ID under PAT/KC flows, service-account UUID under client-credentials.

Permission matrix

Endpoint Required permission
GET /api/kb/folders kb:read:pages
GET /api/kb/article kb:read:pages
GET /api/kb/search kb:read:pages
GET /api/kb/blobs/[id] kb:read:assets
GET /api/kb/events kb:read:pages (via ?token=…)
GET /api/kb/widget.css public
POST /api/kb/admin/articles kb:write:pages
POST /api/kb/admin/articles/import kb:write:pages
PUT /api/kb/admin/articles/[id]/draft kb:write:pages
POST /api/kb/admin/articles/[id]/publish kb:manage:pages
POST /api/kb/admin/articles/[id]/archive kb:manage:pages
DELETE /api/kb/admin/articles/[id] kb:manage:pages
POST /api/kb/admin/folders kb:manage:folders
PUT /api/kb/admin/folders/[id] kb:manage:folders
DELETE /api/kb/admin/folders/[id] kb:manage:folders
POST /api/kb/admin/blobs kb:write:assets
GET/POST /api/kb/admin/tokens kb:manage:groups
DELETE /api/kb/admin/tokens/[id] kb:manage:groups
GET /api/kb/admin/tokens/[id]/usage kb:manage:groups

Permissions are Wiki.js-style. Roles map to KC groups (kb-readers, kb-editors, kb-managers, kb-admins). Path-scoped overrides live in kb_page_rule and apply only to session/Bearer calls — PATs and the guest principal always carry groups: [], which short-circuits the rule branch (rules cannot grant or revoke PAT / guest access).


Errors

Status When
400 Missing required field, unknown folderSlug, parse fail
401 Missing / invalid / expired Bearer token
403 Token valid but lacks the required permission
404 Article id / slug not found

A 401 mid-session under PAT or KC flow means the token expired — re-fetch it from the source. Under client-credentials, re-run the grant.


Where to go next

  • Human-facing usageuser guide
  • Full agent-API referenceAGENT-API.md in the repo
  • OpenAPI specGET /api/openapi.yaml on each deployment
On this page · 10 sections