Skip to main content
Documentation

Knowledge Base

The Knowledge Base service is hosted help documentation for end users. Define categories, sections, articles, and FAQs through the API or MCP tools. Minolith hosts the rendered help centre at kb.minolith.io/{slug} with theming and search, and exposes a site-key-authenticated public API for SPA consumption.

Base URL: https://api.minolith.io/v1/kb

Authentication: All authenticated endpoints require an API key via the Authorization header:

Authorization: Bearer mlth_your_api_key_here

The public API (see Public API) does NOT use a bearer token — it uses a domain-locked site key instead.

Pricing:

Action Credits
Knowledge base created 3
Category created 2
Section created 1
Article created 3
FAQ entry created 1
All updates, reads, deletes, publish/archive, search, site-key management Free

Core Concepts

Knowledge Base — Top-level container. A project can have multiple (e.g. one per product). Has a slug, optional publishing toggle, theme, and public site key.

Category — Top-level grouping for content (e.g. "Getting Started", "Billing"). Optional icon.

Section — Optional sub-grouping inside a category. Articles and FAQs can live directly in a category or inside a section.

Article — Markdown help article with title, body, excerpt, tags, and cross-referenced related articles. Has a status: draft, published, archived.

FAQ — Lightweight question/answer pair. Rendered as an accordion on the hosted page.

Public Site Key (psk_...) — Auto-generated when you create a KB. Required for every call to the public API. Revoke and rotate via dashboard or API.

Allowed domains (shared) — The public API enforces a domain allow-list, but it is not stored on the knowledge base. The same project-level list used by the Changelog widget and Feedback widget is applied to KB public API requests. Edit it under Changelog settings (/projects/:id/changelog/settings).

is_public applies only to the hosted page. When is_public is false the hosted page at kb.minolith.io/{slug} returns 404, but the public API is still available for SPA consumption. Use this to ship purely via API without the hosted page.


Limits

Limit Value
Slug 200 chars, lowercase alphanumeric + hyphens, unique per project
Title 500 chars
Description 10,000 chars
Category name 200 chars
Section name 200 chars
Article title 500 chars
Article body 100,000 chars
Article excerpt 500 chars
Article tags 20, each max 100 chars
Article related_articles 10 IDs
FAQ question 500 chars
FAQ answer 10,000 chars
Site key rate limit 1-1000 requests/minute (default 100)
Bulk article operations 20 per call

ID Prefixes

Prefix Entity
kb_ Knowledge base
psk_ Public site key
kbc_ Category
kbs_ Section
kba_ Article
kbf_ FAQ

Knowledge Bases

Create knowledge base

POST /v1/kb/ — [3 credits]

Request:

{
  "title": "Studbook.farm Help Centre",
  "slug": "studbook-help",
  "description": "Everything you need to know...",
  "is_public": false,
  "theme": "minimal"
}

slug is required and must be unique within your project. theme is one of minimal, developer, brand, docs, bare — default minimal. is_public controls the hosted page only; the public API works either way once you have a site key.

Response (201):

{
  "data": {
    "id": "kb_a1b2c3d4e5f6",
    "title": "Studbook.farm Help Centre",
    "slug": "studbook-help",
    "description": "Everything you need to know...",
    "is_public": false,
    "shared_allowed_domains": ["studbook.farm", "www.studbook.farm"],
    "theme": "minimal",
    "theme_settings": null,
    "logo_path": null,
    "site_key": "psk_x9y8z7w6v5u4t3s2r1q0p9o8n7m6",
    "public_url": null,
    "created_at": "2026-04-22 14:00:00",
    "updated_at": "2026-04-22 14:00:00"
  }
}

shared_allowed_domains is read-only on KB responses — it reflects the project-level list stored on Changelog settings. Update that list at /projects/:id/changelog/settings.

IMPORTANT: The site_key field is returned only on create and on revoke. Store it immediately — there is no API to retrieve the full key again.

List knowledge bases

GET /v1/kb/ — free

Supports ?is_public=true|false, plus standard cursor pagination (limit, cursor).

Each item also includes category_count, section_count, article_count, faq_count.

Get knowledge base

GET /v1/kb/:id — free

Update knowledge base

PUT /v1/kb/:id — free

All fields optional. Same shape as create. Changing slug breaks existing hosted URLs and public API URLs.

Delete knowledge base

DELETE /v1/kb/:id — free

Cascades all categories, sections, articles, FAQs, and site keys.


Site Key Management

Get the active site key

GET /v1/kb/:id/site-key — free

Returns only the key prefix and metadata — the full plaintext key is never returned after creation.

{
  "data": {
    "id": "psk_abc123def456",
    "kb_id": "kb_a1b2c3d4e5f6",
    "key_prefix": "psk_x9y8z7w6",
    "rate_limit": 100,
    "created_at": "2026-04-22 14:00:00",
    "revoked_at": null
  }
}

Revoke and regenerate

POST /v1/kb/:id/site-key/revoke — free

Revokes the current key immediately and issues a new one. The new plaintext key is returned once in the response — store it immediately.

{
  "data": {
    "id": "psk_newidxxx",
    "site_key": "psk_newkeyxxxxxxxxxxxxxxxxxxxxxxxx",
    "key_prefix": "psk_newkey",
    "rate_limit": 100,
    "created_at": "2026-04-22 14:05:00"
  }
}

Update rate limit

PUT /v1/kb/:id/site-key — free

{ "rate_limit": 200 }

Accepted range: 1-1000 requests/minute per key.


Categories

Create

POST /v1/kb/:kb_id/categories — [2 credits]

{
  "name": "Getting Started",
  "description": "Intro to the app.",
  "icon": "book",
  "sort_order": 10
}

List

GET /v1/kb/:kb_id/categories — free

Get / Update / Delete

GET|PUT|DELETE /v1/kb/:kb_id/categories/:id — free

Deleting cascades sections, articles, and FAQs.


Sections

Create

POST /v1/kb/:kb_id/sections — [1 credit]

{
  "category_id": "kbc_abc123",
  "name": "Your First Animal",
  "description": "Step-by-step for first-time users.",
  "sort_order": 10
}

List

GET /v1/kb/:kb_id/sections?category_id=kbc_xxx — free

Get / Update / Delete

GET|PUT|DELETE /v1/kb/:kb_id/sections/:id — free

Deleting a section sets section_id on its articles/FAQs to null (they remain in the category).


Articles

Create

POST /v1/kb/:kb_id/articles — [3 credits]

{
  "category_id": "kbc_abc123",
  "section_id": "kbs_def456",
  "title": "How to register a new animal",
  "body": "# Registering a New Animal\n\n...",
  "excerpt": "Step-by-step guide.",
  "status": "draft",
  "tags": ["animals", "getting-started"],
  "related_articles": ["kba_xyz789"]
}

status is draft (default), published, or archived. Setting status to published stamps published_at on the server.

List

GET /v1/kb/:kb_id/articles — free

Supports ?category_id, ?section_id (or null/ungrouped for category-level articles), ?status (CSV), ?tag, plus cursor pagination. Body is omitted in list responses — use GET /:id for the full article.

Get / Update / Delete

GET|PUT|DELETE /v1/kb/:kb_id/articles/:id — free

Publish / Archive

POST /v1/kb/:kb_id/articles/:id/publish — free POST /v1/kb/:kb_id/articles/:id/archive — free

Bulk

POST /v1/kb/:kb_id/articles/bulk — [3 credits each] PUT /v1/kb/:kb_id/articles/bulk — free

Body: {"articles": [ { ...article}, ... ]}, up to 20 per call. All articles are validated before any are inserted — a validation failure on any one aborts the entire batch.


FAQ Entries

Create

POST /v1/kb/:kb_id/faqs — [1 credit]

{
  "category_id": "kbc_abc123",
  "section_id": "kbs_def456",
  "question": "How do I reset my password?",
  "answer": "Click **Forgot password** on the login page and check your inbox.",
  "status": "published"
}

List / Get / Update / Delete / Publish

GET|PUT|DELETE /v1/kb/:kb_id/faqs/:id — free POST /v1/kb/:kb_id/faqs/:id/publish — free


GET /v1/kb/:kb_id/search?q=... — free

Matches on article titles, bodies, excerpts, FAQ questions, and FAQ answers. By default includes drafts (authenticated users can see their own drafts); add ?status=published to restrict.

{
  "data": {
    "articles": [
      {
        "id": "kba_xyz",
        "type": "article",
        "title": "How to register a new animal",
        "excerpt": "...",
        "category_id": "kbc_abc",
        "section_id": "kbs_def"
      }
    ],
    "faqs": [
      {
        "id": "kbf_abc",
        "type": "faq",
        "question": "How do I reset my password?",
        "excerpt": "Click **Forgot password**...",
        "category_id": "kbc_abc",
        "section_id": null
      }
    ]
  }
}

Public API

The public API lets your SPA fetch help content without exposing your Minolith API key.

Base URL: https://api.minolith.io/v1/kb/public/{slug}

Required header: X-Minolith-Site-Key: psk_...

Required request origin: The Origin or Referer header must match one of the project's shared allowed_domains entries (the list is managed under Changelog settings and shared across Changelog/Feedback/KB).

Rate limit: Configurable per site key (1-1000 req/min, default 100). Standard rate-limit headers are set.

All three checks must pass for any public API request:

  1. X-Minolith-Site-Key header present, key exists and is not revoked
  2. Origin/Referer matches the project's shared allowed-domain list (empty list = allow all)
  3. Rate limit not exceeded

A failure on any check returns a generic 403 Access denied (no info on which check failed) except rate-limit which is 429 Rate limit exceeded. Draft and archived content is never returned.

The KB's is_public flag is not checked here — the public API remains available even when the hosted page is disabled.

SPA example

const KB_SLUG = 'studbook-help';
const SITE_KEY = 'psk_a1b2c3d4e5f6...';

async function loadStructure() {
  const r = await fetch(`https://api.minolith.io/v1/kb/public/${KB_SLUG}/structure`, {
    headers: { 'X-Minolith-Site-Key': SITE_KEY },
  });
  if (!r.ok) throw new Error('Failed to load help centre');
  return (await r.json()).data;
}

async function loadArticle(id) {
  const r = await fetch(`https://api.minolith.io/v1/kb/public/${KB_SLUG}/articles/${id}`, {
    headers: { 'X-Minolith-Site-Key': SITE_KEY },
  });
  return (await r.json()).data;
}

Public endpoints

GET /v1/kb/public/{slug}/structure           Full navigation tree (categories → sections → article titles)
GET /v1/kb/public/{slug}/categories          Categories with article + FAQ counts
GET /v1/kb/public/{slug}/categories/{id}     Category detail with sections, articles, FAQs
GET /v1/kb/public/{slug}/articles/{id}       Full article (only if published)
GET /v1/kb/public/{slug}/faqs/{category_id}  FAQs for a category
GET /v1/kb/public/{slug}/search?q=term       Search published content

Structure response

{
  "data": {
    "title": "Studbook.farm Help Centre",
    "description": "Everything you need...",
    "slug": "studbook-help",
    "categories": [
      {
        "id": "kbc_abc",
        "name": "Getting Started",
        "icon": "book",
        "sections": [
          { "id": "kbs_def", "name": "First Animal", "articles": [ { "id": "kba_xyz", "title": "How to...", "excerpt": "..." } ] }
        ],
        "articles": [],
        "faq_count": 3
      }
    ]
  }
}

Article response

Related articles are resolved to title + excerpt so the SPA can render "See also" without extra calls.

{
  "data": {
    "id": "kba_xyz",
    "title": "How to register a new animal",
    "body": "# Registering a New Animal\n\n...",
    "excerpt": "...",
    "tags": ["animals"],
    "related_articles": [ { "id": "kba_def", "title": "CSV Import", "excerpt": "..." } ],
    "category": { "id": "kbc_abc", "name": "Getting Started" },
    "section": { "id": "kbs_def", "name": "First Animal" },
    "published_at": "2026-04-22 14:00:00",
    "updated_at": "2026-04-22 14:00:00"
  }
}

Hosted Page

When is_public is true, a live help centre is served at https://kb.minolith.io/{slug}. It renders through one of five themes (minimal, developer, brand, docs, bare) and is cached in Redis; any write operation invalidates the cache.

When is_public is false, the hosted URL returns 404 but the API, MCP tools, and dashboard continue to work normally. Use this to build your content privately before launch, or to use the service purely as an internal API without a hosted page.

Theme settings

Set theme_settings on the knowledge base to customise appearance:

{
  "primary_color": "#4f46e5",
  "dark_mode": false,
  "custom_css": ".kb-content h1 { letter-spacing: -0.02em; }"
}

primary_color must be a valid hex colour. custom_css max 50,000 chars. dark_mode applies to the brand theme.


Error Codes

Code HTTP Meaning
kb_not_found 404 Knowledge base not found in this project.
category_not_found 404 Category not found.
section_not_found 404 Section not found.
article_not_found 404 Article not found.
faq_not_found 404 FAQ not found.
site_key_not_found 404 No active site key for this knowledge base.
validation_error 422 Field-level validation failure.
forbidden 403 Public API: site key invalid, revoked, or domain not allowed.
rate_limit_exceeded 429 Too many requests for this site key.
spending_cap_exceeded 402 Account monthly cap would be exceeded.

Last updated: 22 Apr 2026