Compendiq provides a RESTful API built on Fastify 5 with auto-generated interactive documentation.
The backend serves interactive Swagger UI documentation at:
http://localhost:3051/api/docs
This is the primary API reference. It is auto-generated from route schemas using @fastify/swagger and @fastify/swagger-ui, and is always up to date with the running server.
Note: Swagger UI is only served in non-production environments. It is disabled when
NODE_ENV=productionto avoid exposing the full API schema.
All endpoints except /api/health and /api/auth/* require a valid JWT Bearer token.
Include the token in the Authorization header:
Authorization: Bearer <access-token>
Access tokens are obtained via the login endpoint and have a configurable expiry (default: 1 hour). Use the refresh endpoint to obtain new access tokens without re-authenticating.
| Prefix | Description |
|---|---|
GET /api/health |
Health checks (live, ready, start probes) |
POST /api/auth/* |
Authentication (register, login, refresh, logout) |
GET/PUT /api/settings |
User settings (Confluence URL, PAT, model selection) |
GET/PUT/DELETE /api/notion/connection |
Notion internal integration token (hasToken only; secret never returned) |
GET /api/notion/tree |
Authenticated Notion workspace tree (pages and databases selectable; a database carries recommendedMode table/pages plus rowContent, isWiki, rowCount, columns; Board layouts and other unsupported types carry skipReason; Board cards remain selectable articles) |
POST /api/notion/import |
Start a one-shot import of selected Notion pages and databases as standalone pages (202 { status: 'importing' }; databaseModes picks skip/table/pages per database). Results are not on this response. |
GET /api/notion/import/status |
Background import job (idle / importing / complete with per-item success / skip / fail / already_imported plus importedAs page/article/table / error) |
GET/POST/PUT/DELETE /api/pages/* |
Page CRUD, versions, tags, embeddings, duplicates, export/import |
GET /api/spaces |
Confluence space listing and selection |
POST /api/sync |
Manual sync trigger |
POST /api/llm/* |
LLM operations (improve, generate, summarize, ask, PDF extract) |
GET /api/embeddings/status |
Embedding pipeline status |
GET/POST/PUT/DELETE /api/templates/* |
Knowledge base templates (personal + admin-global) |
GET/POST /api/comments/* |
Page comments |
GET /api/analytics/* |
Content analytics and search analytics |
GET/POST /api/verification/* |
Page verification/review workflow |
GET/POST /api/notifications/* |
User notifications |
GET/POST /api/admin/* |
Admin operations (key rotation, audit log, LLM settings, OIDC, RBAC) |
curl http://localhost:3051/api/healthResponse:
{
"status": "ok",
"services": {
"postgres": true,
"redis": true,
"llm": true
},
"llmProvider": "OpenAI Prod",
"version": "0.6.2",
"uptime": 3600.123
}
/api/healthis public (unauthenticated). It reports only coarse status plus the provider name and build version the frontend needs. Internal operational telemetry (per-provider circuit-breaker state, LLM/BullMQ queue metrics) is not exposed here.
curl -X POST http://localhost:3051/api/auth/register \
-H "Content-Type: application/json" \
-d '{
"username": "admin",
"email": "admin@example.com",
"password": "your-secure-password"
}'Response:
{
"user": {
"id": "uuid",
"username": "admin",
"email": "admin@example.com",
"role": "admin"
},
"accessToken": "eyJhbG...",
"refreshToken": "eyJhbG..."
}curl -X POST http://localhost:3051/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"username": "admin",
"password": "your-secure-password"
}'curl -X POST http://localhost:3051/api/auth/refresh \
-H "Content-Type: application/json" \
-d '{
"refreshToken": "eyJhbG..."
}'curl http://localhost:3051/api/pages \
-H "Authorization: Bearer <access-token>"curl http://localhost:3051/api/pages/<page-id> \
-H "Authorization: Bearer <access-token>"GET /api/pages/:id/connections returns the article's linked, section, and
related groups. IDs on this endpoint are numeric pages.id values, serialized
as strings. Each item contains pageId, title, and a reasons array:
| Persisted type | Evidence on the response |
|---|---|
explicit_link |
direction: incoming or outgoing, verified against the current link content |
parent_child |
direction: parent or child, verified against the current hierarchy |
embedding_similarity |
The persisted numeric score |
label_overlap |
Actual shared labels and the persisted numeric score |
Related recommendations contain at most five unique targets, ranked by their highest persisted evidence score, then numeric page ID. Reciprocal similarity rows contribute one target with the maximum score. Direct and hierarchy targets are not repeated as semantic recommendations. An empty response contains three empty arrays; unavailable source pages return 404. Source and target visibility checks precede recommendation ranking.
Deterministic relationship materialization is independent of embeddings. Page mutations invalidate deterministic evidence in the database; the authorized Connections and focused-local-graph read paths settle pending materialization through the shared engine before returning results. Unchanged reads do not rebuild the corpus, and failures are reported rather than converted to empty groups. Semantic scores are not rewritten by deterministic-only work.
POST /api/pages/:id/connections/events collects authenticated panel usage:
{"event":"impression","visitId":"f72f706e-a3b4-46cb-91f0-926558a03d68"}The other event bodies are {"event":"graph_launch","visitId":"<uuid>"} and
{"event":"connection_click","visitId":"<uuid>","targetPageId":"42","group":"related"}.
The group is linked, section, or related. Requests use the shared strict
ConnectionEventSchema; successful collection returns {"recorded":true}.
Never send titles, labels, relationship explanations, scores, or article bodies
as analytics metadata. See the administrator guide for impression semantics and
the four-week adoption review.
curl "http://localhost:3051/api/search?q=deployment&mode=hybrid" \
-H "Authorization: Bearer <access-token>"Every search item includes source: "standalone" | "confluence", taken from
the current page record in every search mode and fallback path. A named
spaceKey or historical confluenceId does not determine provenance.
curl http://localhost:3051/api/health/readyResponse:
{
"status": "ok",
"services": {
"postgres": true,
"redis": true
},
"version": "0.6.2",
"uptime": 3600.123
}The API enforces rate limiting:
- Global: 300 requests per minute per IP (default; configurable by admins)
- Admin endpoints: stricter limits
- LLM endpoints: stricter limits (due to resource intensity)
Exceeding the rate limit returns 429 Too Many Requests.
Errors follow a consistent format:
{
"statusCode": 400,
"error": "Bad Request",
"message": "Description of what went wrong"
}Common status codes:
| Code | Meaning |
|---|---|
400 |
Bad Request -- invalid input |
401 |
Unauthorized -- missing or invalid token |
403 |
Forbidden -- insufficient permissions |
404 |
Not Found |
429 |
Too Many Requests -- rate limited |
500 |
Internal Server Error |
503 |
Service Unavailable -- dependency down |