Skip to content

Alkiviades — API Reference

Base URL: https://api.alkiviades.cc

Authentication

All protected endpoints require Authorization: Bearer <jwt_access_token> header.

POST /auth/login

Authenticate and receive JWT + refresh cookie.

Body: {"email": "user@example.com", "password": "secret"} Response (200):

{
  "access_token": "eyJ...",
  "token_type": "bearer",
  "is_admin": false,
  "role": "user"
}
Sets httpOnly refresh_token cookie (path: /auth/refresh, 7 days).

Errors: 401 Invalid credentials, 422 Validation error.

POST /auth/refresh

Exchange refresh cookie for new access token.

Requires: refresh_token cookie. Response (200): {"access_token": "eyJ...", "token_type": "bearer"} Rotates refresh token (old revoked, new issued). Errors: 401 Invalid/expired/revoked refresh token.

POST /auth/logout

Revoke refresh token and clear cookie.

Requires: refresh_token cookie (optional, idempotent). Response (200): {"status": "ok"}

POST /auth/guest

Create temporary guest account with 5 queries.

Response (200):

{
  "access_token": "eyJ...",
  "token_type": "bearer",
  "is_admin": false,
  "role": "guest",
  "queries_remaining": 5
}

GET /auth/me

Get current user profile. Auth required.

Response (200):

{
  "id": "uuid",
  "email": "user@example.com",
  "role": "user",
  "is_admin": false,
  "queries_remaining": null
}

Health

GET /health

Public health check.

Response: {"status": "ok"}

GET /health/debug

Admin-only system diagnostics. Admin auth required.

Response:

{
  "python_version": "3.12.x",
  "has_deepseek_key": true,
  "has_tavily_key": true,
  "kb_exists": true,
  "kb_size_mb": 75.2,
  "embed_dtype": "float16",
  "max_vocab_size": 15000,
  "vectors_mmap": true,
  "env": { "RENDER": "true", ... }
}

Chat

POST /chat

Non-streaming chat. Auth required.

Body:

{
  "message": "What are the key positions on healthcare reform?",
  "session_id": "optional-session-uuid",
  "axes": { "ethical": -0.33, "alignment": 0.67, "social": 1.0 }
}

Response (200):

{
  "reply": "Based on the analysis...",
  "session_id": "session-uuid",
  "sources": ["file1.md", "file2.md"],
  "queries_remaining": null
}

Errors: 429 Guest query limit reached. 401 Unauthorized.

POST /chat/stream

SSE streaming chat. Auth required.

Body: Same as /chat.

Response: text/event-stream with events: | Event | Data | Description | |---|---|---| | source | {"file": "doc.md", "score": 0.89} | RAG source chunk | | token | {"text": "word"} | LLM text token | | tool_call | {"tool": "search_news", "args": {...}} | Tool invocation | | tool_result | {"tool": "search_news", "result": "..."} | Tool result | | error | {"message": "..."} | Server error | | done | {"session_id": "uuid"} | Stream complete | | heartbeat | {"t": 1234567890} | Keepalive (every 15s) |

Sessions

All session endpoints require auth.

GET /sessions

List sessions for authenticated user.

Response: [{ "id": "uuid", "title": "Chat about...", "created": "...", "updated": "...", "turns": 5, "axes": "..." }]

GET /sessions/{id}

Get full session with all turns.

Response: { "id": "uuid", "title": "...", "messages": [...] } Query ?preview=true for first 3 turns only.

DELETE /sessions/{id}

Delete a session. 404 if not found or not owned.

Axes

GET /api/axes

Get current axis values.

Response: {"ethical": -1.0, "alignment": 1.0, "social": 1.0}

PUT /api/axes

Update axis values (clamped -1 to 1).

Body: {"ethical": 0.0, "social": -0.5} Response: Updated axes dict.

Admin

GET /api/admin/logs?limit=100

Get last N lines of application log. Admin auth required.

Response: {"lines": "log content..."}

GET /api/admin/logs/stream

SSE stream of new log entries. Admin auth required.

Events: {"event": "log", "data": "log line"} (polled every 2s).

POST /rebuild

Rebuild knowledge base from source files. Admin auth required.

Response: {"chunks": 2461, "diff": {"summary": {...}, "added": {...}, "removed": {...}, "changed": {...}}}