Files
itworx-knowledge-hub-Public/docs/09-api-contract.md
T

6.3 KiB

API contract

The API is built for the web client and trusted scoped automation. A future separate MCP server consumes the same API.

The draft OpenAPI document is in docs/api/openapi.yaml. Implementation and contract tests must keep it current.

Conventions

  • Base path: /api/v1
  • JSON: UTF-8, camelCase
  • IDs: opaque UUID strings
  • Times: RFC 3339 UTC
  • Errors: application/problem+json (RFC 7807)
  • Pagination: opaque cursor
  • Concurrency: strong ETag plus If-Match
  • Correlation: accept/return X-Correlation-ID
  • Idempotency: Idempotency-Key for retryable create/append endpoints
  • Locale: server returns invariant values plus localized UI copy remains client-side

Authentication

Browser

Secure cookie session plus anti-forgery token for mutations.

Service account

Authorization: Bearer ikh_<public-id>_<secret>

The full token is shown once. The server stores:

  • service account ID;
  • token public ID/prefix;
  • SHA-256 or keyed verifier of a cryptographically random 256-bit secret;
  • created/last-used/expiry/revoked timestamps;
  • scopes and optional allowed network policy.

For controlled curation, pages:read authorizes page detail, roots, children, revisions and backlinks; pages:write authorizes create, update, move and reversible trash. Browser callers retain their role-based cookie policy. Update and move accept an optional Idempotency-Key in addition to mandatory If-Match, so bounded automation can retry without duplicating a successful mutation.

The API never returns or logs the secret.

Core scopes

knowledge:read
knowledge:search
pages:read
pages:write
projects:read
projects:write
project-updates:append
decisions:read
decisions:write
runbooks:read
attachments:read
attachments:write
imports:read
imports:execute
audit:read
system:read

Scopes never bypass workspace/content authorization.

Resource endpoints

System

  • GET /system/info
  • GET /health is outside /api/v1 and contains no private details
  • GET /search?q=&types=&tags=&projectId=&cursor=&limit=

Returns permission-filtered hits and facets. No inaccessible counts or snippets.

Pages

  • GET /pages/{id}
  • GET /pages/{id}/children
  • GET /pages/{id}/relations
  • GET /pages/{id}/revisions
  • GET /pages/{id}/revisions/{revision}
  • POST /pages
  • PUT /pages/{id} with If-Match
  • POST /pages/{id}/move with If-Match
  • POST /pages/{id}/trash with If-Match
  • POST /pages/{id}/restore
  • POST /pages/{id}/restore-revision

Page responses include:

  • stable ID;
  • current title/slug/breadcrumb;
  • raw Markdown when scope permits;
  • typed metadata;
  • current revision;
  • ETag;
  • stable source citation object.

Projects

  • GET /projects
  • GET /projects/{id}
  • POST /projects
  • PUT /projects/{id} with If-Match
  • GET /projects/{id}/updates
  • POST /projects/{id}/updates
  • GET /projects/{id}/decisions
  • GET /projects/{id}/context

Project context

GET /projects/{id}/context returns a bounded context bundle:

{
  "project": {
    "id": "...",
    "name": "Ludarium",
    "status": "active",
    "summary": "...",
    "currentStateMarkdown": "...",
    "updatedAt": "..."
  },
  "recentUpdates": [],
  "activeMilestones": [],
  "decisions": [],
  "runbooks": [],
  "documents": [],
  "knownIssues": [],
  "citations": [],
  "truncation": {
    "truncated": false,
    "omittedCounts": {}
  }
}

Requirements:

  • default bounded item counts;
  • explicit updatedAt and source IDs;
  • deterministic ordering;
  • permission filtering before assembly;
  • maximum serialized size;
  • no secrets, raw attachment bodies or private filesystem paths;
  • truncation metadata rather than silent omission.

RAG publication manifest

GET /rag/publication-manifest?space=&cursor=&limit= — a single, deliberately narrow read-only endpoint for RAGcore's n8n sync, reusing the existing pages:read scope (no new scope). There is no general GET /pages enumeration in this API — only /pages/roots plus a tree walk — so this is the one reliable way an external consumer discovers the full curated publication set. Selection and shape are entirely driven by the rag:publish/rag:space:/rag:allow-deprecated/locale: tag convention; see docs/32-rag-publication-convention.md for the full convention and docs/api/openapi.yaml's /rag/publication-manifest path for the exact response shape.

Attachments

The implemented v1 routes accept either the normal cookie role or the matching least-privilege service-account scope: attachments:read for listing/content and attachments:write for upload. pages:write also authorizes move and the reversible POST /pages/{id}/trash operation so an importer can roll back only the pages from its own recorded batch; it does not grant permanent purge access.

  • POST /attachments streaming multipart
  • GET /attachments/{id}/metadata
  • GET /attachments/{id}/content
  • DELETE /attachments/{id} logical delete

Range requests may be supported after authorization. Unsafe types download as attachment.

Imports

Administrative API only:

  • create discovery/preflight;
  • retrieve plan/report;
  • execute accepted plan;
  • validate;
  • rollback.

Source paths are configured server-side; clients cannot supply arbitrary unrestricted host paths.

Service accounts

Administrative browser/API endpoints create, list, rotate and revoke accounts/tokens. Creation/rotation returns the secret once.

Error types

At minimum:

  • validation error;
  • authentication required;
  • forbidden;
  • not found (without existence leakage);
  • concurrency conflict;
  • idempotency conflict;
  • rate limited;
  • unsupported media type;
  • payload too large;
  • import preflight conflict;
  • dependency unavailable.

Every problem includes a stable machine-readable type and correlation ID.

Rate limits

Separate policies:

  • login/recovery/bootstrap;
  • service-token authentication failures;
  • search;
  • attachment upload;
  • import/backup operations;
  • general API.

Limits are configurable and return standards-compatible retry guidance.

Versioning policy

  • Additive compatible fields may appear within v1.
  • Clients must ignore unknown fields.
  • Breaking changes require /api/v2 or a documented transition.
  • Outbox event payloads carry their own schema version.
  • The future MCP server may never query the database directly.