Skip to content

REST API overview

import { Aside } from ‘@astrojs/starlight/components’;

The REST API under /api/v1/ lets integrations and tools (including the Obsidian plugin) manage documents and collections with a stable JSON interface. This page explains behavior that applies to all endpoints. Per-operation fields and request bodies are documented in the OpenAPI reference.

Use the same origin as your deployed Orimora instance:

  • Production: https://your-domain.example.com
  • Local (Vite): http://localhost:5173
  • Docker Compose (app service): http://localhost:3000 unless you remap the port

All paths in this document are relative to that base (e.g. GET /api/v1/documents).

  1. In the web app, go to Settings → Developers and create an API key.
  2. Keys are shown once and have the prefix kb_.
  3. Send the key as a Bearer token:
GET /api/v1/documents HTTP/1.1
Host: your-domain.example.com
Authorization: Bearer kb_your_key_here

The server resolves the key to a user and team. Routes that use requireUser() accept either a normal browser session or this Bearer token.

Each key has one or more scopes:

ScopeTypical use
readList and fetch documents and collections
writeCreate and update resources
adminOperations that change team-level settings (when exposed)

Default for new keys is read + write. Design integrations so they use the smallest scope necessary.

AreaLimit (indicative)Notes
All /api/* routes100 requests per IP or user per 60 secondsGlobal guard in the request pipeline; skipped for GET /api/health
Auth endpointsStricter (e.g. magic link / OAuth)Protects against abuse; see implementation for exact keys

When limited, the response is 429 Too Many Requests with JSON similar to:

{
"error": "Too many requests",
"retryAfterSeconds": 60
}

Successful responses may include:

  • X-RateLimit-Limit — max requests per window
  • X-RateLimit-Remaining — remaining in current window
  • Retry-After — present on 429

Treat 429 with exponential backoff in automation.

Without updatedSince, list responses include:

FieldMeaning
dataArray of documents
totalTotal rows matching filters (not just this page)
limitPage size (capped, default 25, max 100)
offsetSkip offset

Use collectionId, status=published|draft, limit, and offset query parameters as needed.

For tools that only need changed documents since a point in time:

  • Pass updatedSince as an ISO 8601 timestamp (e.g. 2026-04-01T12:00:00.000Z).
  • The response shape differs: there is no total/offset pagination in the same way; the server uses a higher effective limit for the sync window.
  • Add format=markdown together with updatedSince to receive a markdownText field per document for plain-text sync (used by the Obsidian selective pull).

See the OpenAPI /api/v1/documents GET operation for details.

  • text: Markdown string — the server converts it to TipTap/ProseMirror JSON internally.
  • content: Raw TipTap JSON if you already have structured content (advanced integrations).

You typically use one or the other, not both, when creating or updating body content.

Success payloads usually look like:

{ "data": { /* resource */ } }

List endpoints return { "data": [ … ] } plus pagination fields as documented per route.

Errors use JSON with an error string (and HTTP 4xx/5xx):

{ "error": "Document not found" }
HTTPTypical cause
401Missing or invalid API key / session
403Authenticated but not allowed (e.g. onboarding incomplete)
404Resource not found or wrong team
429Rate limited
400Validation / bad JSON

Document lifecycle events can trigger webhooks configured in the workspace. Webhook delivery, signing, and retries are not part of the /api/v1/ CRUD surface; configure them in the app UI. Automation should still use idempotent handlers and verify signatures when provided.

The same operations are available as OpenAPI 3.1 for code generators and the Starlight UI:

Regenerate the bundled spec with:

Terminal window
cd docs && yarn generate:openapi

(Runs automatically as part of yarn build in the docs package.)