Base URL
http://localhost:8001/api/v1
Authentication
All endpoints except /auth/register, /auth/login, and /health require a JWT bearer token:
Authorization: Bearer <token>
Tokens are issued on registration and login, expire after 60 minutes (configurable via JWT_EXPIRE_MINUTES), and carry the user's ID and role in the payload.
Auth
Register
Create a new user account and receive a JWT token.
/api/v1/auth/registerRequest:
{
"username": "admin",
"password": "secure-password-here",
"role": "admin"
}
| Field | Type | Required | Description |
|---|---|---|---|
username | string | Yes | Unique username (max 150 chars) |
password | string | Yes | Password (8-200 chars) |
role | string | No | admin, manager, or user (default: user) |
Response (200):
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "bearer",
"user": {
"id": 1,
"username": "admin",
"role": "admin",
"created_at": "2026-02-15T10:30:00Z"
}
}
Errors:
| Status | Detail |
|---|---|
| 400 | Username already exists |
Login
Authenticate an existing user and receive a JWT token.
/api/v1/auth/loginRequest:
{
"username": "admin",
"password": "secure-password-here"
}
Response (200):
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "bearer",
"user": {
"id": 1,
"username": "admin",
"role": "admin",
"created_at": "2026-02-15T10:30:00Z"
}
}
Errors:
| Status | Detail |
|---|---|
| 401 | Invalid credentials |
Chat
Send Message
Send a message and receive a streaming SSE response. Creates a new conversation if conversation_id is not provided.
/api/v1/chat/completionsRequest:
{
"message": "What is Abu Dhabi's AI strategy?",
"conversation_id": "abc-123",
"knowledge_base_id": "kb-001",
"model": "llama-3.3-70b-versatile"
}
| Field | Type | Required | Description |
|---|---|---|---|
message | string | Yes | User message (max 100,000 chars) |
conversation_id | string | No | Resume an existing conversation |
knowledge_base_id | string | No | Scope RAG retrieval to this knowledge base |
model | string | No | Target model (auto-resolves provider) |
Response (200, text/event-stream):
data: {"token": "Abu", "conversation_id": "abc-123"}
data: {"token": " Dhabi", "conversation_id": "abc-123"}
data: {"token": "'s", "conversation_id": "abc-123"}
...
data: {"done": true, "conversation_id": "abc-123"}
Auth: Bearer token required.
Errors:
| Status | Detail |
|---|---|
| 401 | Invalid token |
| 404 | Conversation not found (if conversation_id provided but does not exist or belongs to another user) |
List Conversations
Retrieve the current user's conversations, ordered by most recently updated.
/api/v1/chat/conversationsQuery Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
skip | integer | 0 | Offset for pagination |
limit | integer | 50 | Max results (1-100) |
Response (200):
[
{
"id": "abc-123",
"title": "What is Abu Dhabi's AI strategy?",
"knowledge_base_id": "kb-001",
"message_count": 4,
"created_at": "2026-02-15T10:30:00Z",
"updated_at": "2026-02-15T10:35:00Z"
}
]
The title is automatically set to the first 50 characters of the initial message.
Export Conversation
Export a conversation as JSON or Markdown with full message history and source citations.
/api/v1/chat/conversations/{conversation_id}/exportQuery Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
format | string | json | Export format: json or markdown |
Response (200):
Returns a downloadable file with Content-Disposition header.
JSON format includes all messages with roles, content, timestamps, and sources. Markdown format renders the conversation as a formatted document with headings, role labels, and source citations.
Errors:
| Status | Detail |
|---|---|
| 404 | Conversation not found |
List Models
List all available models and their providers.
/api/v1/chat/modelsResponse (200):
[
{"id": "llama-3.3-70b-versatile", "provider": "groq"},
{"id": "llama-3.1-8b-instant", "provider": "groq"},
{"id": "allam-2-7b", "provider": "groq"},
{"id": "qwen/qwen3-32b", "provider": "groq"},
{"id": "meta-llama/llama-4-scout-17b-16e-instruct", "provider": "groq"},
{"id": "meta-llama/llama-4-maverick-17b-128e-instruct", "provider": "groq"},
{"id": "openai/gpt-oss-120b", "provider": "groq"},
{"id": "gpt-4o-uaenorth", "provider": "azure"},
{"id": "gpt-4o", "provider": "core42"},
{"id": "gpt-4.1", "provider": "core42"}
]
Knowledge Bases
Create Knowledge Base
Create a new knowledge base for document storage and RAG retrieval.
/api/v1/knowledge-basesAuth: Requires admin or manager role.
Request:
{
"name": "UAE AI Policy",
"description": "National AI strategy and governance documents"
}
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Knowledge base name (max 500 chars) |
description | string | No | Description (max 5000 chars) |
Response (200):
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "UAE AI Policy",
"description": "National AI strategy and governance documents",
"document_count": 0,
"owner_id": 1,
"created_at": "2026-02-15T10:30:00Z"
}
List Knowledge Bases
List knowledge bases visible to the current user. Admins see all knowledge bases; other roles see only their own.
/api/v1/knowledge-basesQuery Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
skip | integer | 0 | Offset for pagination |
limit | integer | 50 | Max results (1-100) |
Response (200):
[
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "UAE AI Policy",
"description": "National AI strategy and governance documents",
"document_count": 3,
"owner_id": 1,
"created_at": "2026-02-15T10:30:00Z"
}
]
Documents
Upload Document
Upload a file to a knowledge base. Triggers the full RAG ingestion pipeline (extraction, chunking, embedding, storage).
/api/v1/documents/uploadAuth: Requires admin or manager role. Managers can only upload to knowledge bases they own.
Content-Type: multipart/form-data
| Field | Type | Required | Description |
|---|---|---|---|
file | file | Yes | PDF, DOCX, or TXT file |
knowledge_base_id | string | Yes | Target knowledge base ID |
Response (200):
{
"id": "doc-001",
"filename": "uae-ai-strategy.pdf",
"content_type": "application/pdf",
"knowledge_base_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"chunk_count": 42,
"created_at": "2026-02-15T10:35:00Z"
}
Errors:
| Status | Detail |
|---|---|
| 400 | Unsupported file type |
| 400 | Could not extract text from document |
| 403 | Not authorized for this knowledge base |
| 404 | Knowledge base not found |
List Documents
List documents, optionally filtered by knowledge base.
/api/v1/documentsQuery Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
knowledge_base_id | string | -- | Filter by knowledge base |
skip | integer | 0 | Offset for pagination |
limit | integer | 50 | Max results (1-100) |
Response (200):
[
{
"id": "doc-001",
"filename": "uae-ai-strategy.pdf",
"content_type": "application/pdf",
"knowledge_base_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"chunk_count": 42,
"created_at": "2026-02-15T10:35:00Z"
}
]
Delete Document
Delete a document and remove all its vector chunks from pgvector.
/api/v1/documents/{document_id}Auth: Requires admin or manager role.
Response (200):
{
"status": "deleted",
"id": "doc-001"
}
Errors:
| Status | Detail |
|---|---|
| 404 | Document not found |
RAG Ingest (Service-to-Service)
Ingest Pre-Processed Chunks
Accept pre-chunked document text from external services (e.g., Anar Docs) and store embeddings in pgvector. Skips text extraction and chunking.
/api/v1/rag/ingestAuth: Requires admin or manager role.
Request:
{
"knowledge_base_id": "kb-001",
"document_id": "doc-from-docs-001",
"filename": "scanned-contract.pdf",
"chunks": [
{
"text": "Article 1: Definitions and scope of this agreement...",
"index": 0,
"metadata": {"page": 1, "section": "definitions"}
},
{
"text": "Article 2: Terms and conditions governing...",
"index": 1,
"metadata": {"page": 2, "section": "terms"}
}
],
"language": "ar"
}
| Field | Type | Required | Description |
|---|---|---|---|
knowledge_base_id | string | Yes | Target knowledge base |
document_id | string | Yes | Document identifier (must be unique) |
filename | string | Yes | Original filename |
chunks | array | Yes | Array of chunk objects (max 1000) |
chunks[].text | string | Yes | Chunk text content (max 100,000 chars) |
chunks[].index | integer | Yes | Chunk position index |
chunks[].metadata | object | No | Arbitrary metadata (stored with embedding) |
language | string | No | Document language: en, ar, or mixed (default: en) |
Response (200):
{
"document_id": "doc-from-docs-001",
"chunks_ingested": 2
}
Errors:
| Status | Detail |
|---|---|
| 400 | No chunks provided |
| 404 | Knowledge base not found |
System
Health Check
/api/v1/healthAuth: None required.
Response (200):
{
"status": "healthy",
"service": "anar-chat"
}
Metrics
/api/v1/metricsAuth: None required.
Response (200):
{
"uptime_seconds": 3600.5,
"requests_total": 1250,
"errors_total": 3,
"chat_completions": 890,
"documents_uploaded": 45
}
Error Response Format
All error responses follow a consistent structure:
{
"detail": "Human-readable error message"
}
| Status Code | Meaning |
|---|---|
| 400 | Bad request (validation error, unsupported file type) |
| 401 | Invalid or missing JWT token |
| 403 | Insufficient role permissions |
| 404 | Resource not found |
| 422 | Request body validation error (Pydantic) |
| 500 | Internal server error |
Rate Limiting
Chat applies rate limiting through the anar_shared.harden() middleware. Requests exceeding the rate limit receive a 429 Too Many Requests response. Configure limits through the shared library's hardening settings.
OpenAPI Schema
The full OpenAPI specification is available at:
http://localhost:8001/openapi.json
Interactive Swagger documentation:
http://localhost:8001/docs