Base URL: http://localhost:8000
Auth: Bearer JWT token in Authorization: Bearer <token> header (all endpoints except /auth/signup and /auth/login)
Content-Type: application/json unless stated otherwise
- Authentication
- Sessions
- Sources (Knowledge Base)
- Chat & Agent
- Skills
- Artifacts
- Admin (Platform Settings)
- Common Error Responses
Create a new user account and receive a JWT token.
Request Body
{
"username": "alice",
"password": "mysecurepass"
}Response 201
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"user_id": "d3f1a2b3-...",
"username": "alice",
"is_admin": false
}Errors
| Status | Detail |
|---|---|
| 409 | Username already exists |
Authenticate and receive a JWT token.
Request Body
{
"username": "admin",
"password": "admin$123"
}Response 200
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"user_id": "a5990d2f-...",
"username": "admin",
"is_admin": true
}Errors
| Status | Detail |
|---|---|
| 401 | Invalid username or password |
Stateless logout (client discards the token).
Response 200
{ "message": "Logged out successfully" }Get the currently authenticated user's profile.
Response 200
{
"id": "a5990d2f-...",
"username": "admin",
"is_active": true,
"is_admin": true,
"created_at": "2026-03-14T10:41:02.500193"
}List all active sessions for the current user.
Query Parameters
| Param | Type | Default | Description |
|---|---|---|---|
| include_archived | bool | false | Include archived sessions |
Response 200
[
{
"id": "ca266e3d-...",
"user_id": "a5990d2f-...",
"title": "My Research Session",
"status": "active",
"llm_model": "gemini",
"created_at": "2026-03-14T10:48:16.400755",
"updated_at": "2026-03-14T10:48:16.400756",
"archived_at": null
}
]List available LLM models for session creation.
Response 200
[
{ "id": "gemini", "name": "Google Gemini (gemini-2.5-flash)" },
{ "id": "azure_openai", "name": "Azure OpenAI GPT (gpt-4o)" }
]Create a new session. The llm_model is locked for the lifetime of the session and cannot be changed after creation.
Request Body
{
"title": "My Research Session",
"llm_model": "gemini"
}llm_model accepted values: gemini (default), azure_openai
Response 201
{
"id": "ca266e3d-...",
"user_id": "a5990d2f-...",
"title": "My Research Session",
"status": "active",
"llm_model": "gemini",
"created_at": "2026-03-14T10:48:16.400755",
"updated_at": "2026-03-14T10:48:16.400756",
"archived_at": null
}Get a single session by ID. Returns 403 if the session belongs to another user.
Response 200 — same shape as POST /sessions response.
Rename or archive a session.
Note: The
llm_modelfield is immutable — it is set at creation time and cannot be changed via PATCH. Attempting to passllm_modelin the request body has no effect.
Request Body (all fields optional)
{
"title": "New Title",
"status": "archived"
}status accepted values: active, archived
Response 200 — updated session object (includes llm_model in response but the value is read-only).
Soft-delete a session (status → deleted). Also deletes the Chroma collection for this session.
Response 200
{ "message": "Session deleted" }All source routes enforce:
- Session ownership (403 if not owned by current user)
- Max 10 files + URLs per session (pasted text is exempt)
List all sources in a session.
Response 200
[
{
"id": "169d28cd-...",
"user_id": "a5990d2f-...",
"session_id": "ca266e3d-...",
"source_type": "pptx",
"display_name": "report.pptx",
"original_filename": "report.pptx",
"source_url": null,
"ingest_status": "complete",
"created_at": "2026-03-14T10:48:52.883226",
"updated_at": "2026-03-14T10:48:52.904848"
}
]source_type values: pdf, pptx, image, txt, text, url
ingest_status values: pending, processing, complete, failed
Upload a file to the session knowledge base.
Content-Type: multipart/form-data
Form Fields
| Field | Type | Description |
|---|---|---|
| file | File | Allowed types: .pdf, .pptx, .jpg, .jpeg, .png, .txt |
Limits
- Max file size:
MAX_UPLOAD_MB(default 50 MB) - Max 10 files+URLs total per session
Response 201
{
"id": "169d28cd-...",
"source_type": "pptx",
"display_name": "report.pptx",
"original_filename": "report.pptx",
"ingest_status": "pending",
"created_at": "2026-03-14T10:48:52.883226",
"updated_at": "2026-03-14T10:48:52.883227"
}Ingestion runs as a background task. Poll GET /sessions/{id}/sources to check ingest_status.
Errors
| Status | Detail |
|---|---|
| 400 | File type not allowed |
| 400 | File too large |
| 400 | Session source limit reached (max 10) |
Add a web URL to the knowledge base. The page is fetched and ingested automatically.
Request Body
{
"url": "https://example.com/article",
"display_name": "Example Article"
}display_name is optional; defaults to the URL.
Response 201 — same shape as file upload response, source_type: "url".
Add pasted text to the knowledge base. Does not count toward the 10-source limit.
Request Body
{
"content": "Your pasted text content here...",
"display_name": "My Notes"
}Max content length: MAX_PASTED_TEXT_CHARS (default 50,000 chars).
Response 201 — same shape, source_type: "text".
Delete a source and remove its vectors from Chroma.
Response 200
{ "message": "Source deleted" }Retrieve the full chat history for a session.
Response 200
[
{
"id": "msg-uuid-...",
"session_id": "ca266e3d-...",
"user_id": "a5990d2f-...",
"role": "user",
"content": "What are the main findings?",
"structured_payload": null,
"created_at": "2026-03-14T11:00:00"
},
{
"id": "msg-uuid-...",
"session_id": "ca266e3d-...",
"user_id": "a5990d2f-...",
"role": "assistant",
"content": "Based on the uploaded documents...",
"structured_payload": {
"citations": [
{
"source_id": "169d28cd-...",
"source_name": "report.pptx",
"chunk_index": 2,
"excerpt": "The key finding was..."
}
],
"plan": ["Retrieve relevant chunks", "Analyse findings", "Compose answer"],
"agent_run_id": "run-uuid-...",
"artifacts": []
},
"created_at": "2026-03-14T11:00:05"
}
]role values: user, assistant, system
Send a message and invoke the agent. This is the primary chat endpoint. The agent uses the LLM model configured at session creation time (session.llm_model — either gemini or azure_openai). The model cannot be changed mid-session.
Request Body
{
"message": "Summarise the key findings from the uploaded report",
"stream": false
}Response 200
{
"message_id": "msg-uuid-...",
"agent_run_id": "run-uuid-...",
"content": "Based on the uploaded report, the key findings are:\n\n1. **Finding 1** [Source 1]...\n2. **Finding 2** [Source 2]...",
"plan": [
"Step 1: Retrieve relevant chunks from knowledge base",
"Step 2: Identify key findings",
"Step 3: Compose structured summary"
],
"citations": [
{
"source_id": "169d28cd-...",
"source_name": "report.pptx",
"chunk_index": 2,
"excerpt": "The key finding was..."
}
],
"clarification_needed": false,
"clarification_question": null,
"artifacts": [
{
"id": "artifact-uuid-...",
"display_name": "generate_pptx_a1b2c3d4",
"artifact_type": "pptx",
"skill_name": "generate_pptx"
}
]
}If the agent needs clarification:
{
"answer": "Could you clarify which report you'd like me to focus on — the Q1 report or the annual review?",
"clarification_needed": true,
"clarification_question": "Which report should I focus on?",
"plan": ["Awaiting clarification from user"],
"citations": []
}Get details of a specific agent run (plan, status, tool runs).
Response 200
{
"id": "run-uuid-...",
"user_id": "a5990d2f-...",
"session_id": "ca266e3d-...",
"status": "complete",
"current_plan": ["Step 1", "Step 2", "Step 3"],
"final_summary": "Summary of what the agent did",
"started_at": "2026-03-14T11:00:00",
"completed_at": "2026-03-14T11:00:05"
}status values: pending, running, complete, failed
All skill routes enforce ownership — users can only access their own skills.
List all skills owned by the current user.
Response 200
[
{
"id": "skill-uuid-...",
"user_id": "a5990d2f-...",
"name": "generate_pptx",
"version": "1.0.0",
"description": "Generates a PowerPoint presentation from structured content",
"validation_status": "valid",
"install_status": "installed",
"is_globally_enabled": true,
"uploaded_at": "2026-03-14T12:00:00",
"updated_at": "2026-03-14T12:00:00",
"validation_errors": null
}
]validation_status values: uploaded, validating, valid, invalid, failed
Upload a skill ZIP file.
Content-Type: multipart/form-data
Form Fields
| Field | Type | Description |
|---|---|---|
| file | File | .zip file containing at minimum SKILL.md |
ZIP requirements
SKILL.mdat top level with YAML frontmatter fields:name,description,version- Only allowed file extensions:
.md,.txt,.py,.json,.yaml,.yml,.js - No path traversal, no dangerous code patterns
- Max size:
MAX_SKILL_ZIP_MB(default 20 MB)
Response 201
{
"id": "skill-uuid-...",
"name": "generate_pptx",
"version": "1.0.0",
"description": "Generates a PowerPoint presentation...",
"validation_status": "valid",
"is_globally_enabled": false,
"uploaded_at": "2026-03-14T12:00:00"
}Errors
| Status | Detail |
|---|---|
| 400 | Not a valid ZIP / validation failed |
| 400 | ZIP too large |
Get skill details including validation errors if any.
Response 200 — same shape as GET /skills list item.
Enable a skill globally for the current user.
Response 200
{ "message": "Skill enabled" }Disable a skill globally.
Response 200
{ "message": "Skill disabled" }Delete a skill and remove its files from storage.
Response 200
{ "message": "Skill deleted" }Enable a skill for a specific session only.
Response 200
{ "message": "Skill enabled for session" }Disable a skill for a specific session only.
Response 200
{ "message": "Skill disabled for session" }List all generated artifacts for a session.
Response 200
[
{
"id": "artifact-uuid-...",
"user_id": "a5990d2f-...",
"session_id": "ca266e3d-...",
"artifact_type": "pdf",
"display_name": "Research Report.pdf",
"created_at": "2026-03-14T12:30:00"
}
]artifact_type values: pdf, csv, xlsx, pptx
Note:
pptxartifacts are created automatically by the agent when a skill (e.g.generate_pptx) executes successfully and produces a file. They are not created via the/generateendpoint.
Generate a downloadable artifact from session content.
Request Body
{
"artifact_type": "pdf",
"display_name": "Research Report",
"content": "# Research Report\n\nThis is the report content...",
"data": null
}For CSV/XLSX, use data instead of content:
{
"artifact_type": "csv",
"display_name": "Findings Table",
"content": null,
"data": [
{"Finding": "Item 1", "Evidence": "Source A", "Confidence": "High"},
{"Finding": "Item 2", "Evidence": "Source B", "Confidence": "Medium"}
]
}Response 201
{
"id": "artifact-uuid-...",
"artifact_type": "pdf",
"display_name": "Research Report.pdf",
"created_at": "2026-03-14T12:30:00"
}Download an artifact file. Validates ownership before serving.
Response 200 — Binary file stream with appropriate Content-Type header.
| Type | Content-Type |
|---|---|
application/pdf |
|
| csv | text/csv |
| xlsx | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet |
| pptx | application/vnd.openxmlformats-officedocument.presentationml.presentation |
All admin routes require the authenticated user to have is_admin = true. Non-admin users receive a 403 Forbidden response.
Get current platform settings including the active embedding model.
Response 200
{
"active_embedding_model": "sentence-transformers/all-MiniLM-L6-v2",
"available_embedding_models": [
{
"id": "sentence-transformers/all-MiniLM-L6-v2",
"name": "MiniLM-L6-v2 (Local, Free)",
"provider": "sentence-transformers"
},
{
"id": "azure-text-embedding-3-small",
"name": "Azure OpenAI text-embedding-3-small",
"provider": "azure_openai"
},
{
"id": "azure-text-embedding-3-large",
"name": "Azure OpenAI text-embedding-3-large",
"provider": "azure_openai"
}
]
}Errors
| Status | Detail |
|---|---|
| 403 | Admin access required |
Change the active embedding model for all platform users. Affects new document ingestion only.
Request Body
{
"model_id": "azure-text-embedding-3-small"
}model_id accepted values: sentence-transformers/all-MiniLM-L6-v2, azure-text-embedding-3-small, azure-text-embedding-3-large
Response 200
{
"message": "Embedding model updated",
"active_embedding_model": "azure-text-embedding-3-small"
}Errors
| Status | Detail |
|---|---|
| 400 | Invalid model |
| 403 | Admin access required |
{ "detail": "Error message here" }| Status | Meaning |
|---|---|
| 400 | Bad request / validation error |
| 401 | Missing or invalid JWT token |
| 403 | Forbidden — resource belongs to another user |
| 404 | Resource not found |
| 409 | Conflict (e.g. duplicate username) |
| 422 | Unprocessable entity (Pydantic validation failure) |
| 500 | Internal server error |
All 4xx/5xx responses use the {"detail": "..."} shape from FastAPI.
Interactive API docs available at http://localhost:8000/docs (Swagger UI)