Base URL: http://localhost:8000
Interactive docs: http://localhost:8000/docs (Swagger UI)
POST /api/chat
Streams an AI response via Server-Sent Events.
Request Body:
{
"conversation_id": "uuid (optional — omit to create new conversation)",
"model": "qwen3.5:9b (optional — uses user default)",
"message": "Hello!",
"image": "base64-encoded image (optional)"
}SSE Events:
| Event Type | Fields | Description |
|---|---|---|
title |
conversation_id, title |
AI-generated chat title (first message only) |
thinking |
content |
Reasoning token from the model |
content |
content |
Response token |
done |
conversation_id, message_id, user_message_id, tokens_used, tokens_per_sec, thinking_duration |
Stream complete |
error |
content |
Error message |
Example SSE stream:
data: {"type": "title", "conversation_id": "abc-123", "title": "Python Snake Game"}
data: {"type": "thinking", "content": "Let me think about this..."}
data: {"type": "content", "content": "Here's how to"}
data: {"type": "content", "content": " build a snake game:"}
data: {"type": "done", "conversation_id": "abc-123", "message_id": "msg-456", "tokens_used": 150, "tokens_per_sec": 45.2}
POST /api/chat/regenerate
Deletes the last assistant message and re-streams a new response.
Request Body:
{
"conversation_id": "uuid",
"message_id": "uuid of assistant message to regenerate",
"model": "optional — override model"
}GET /api/conversations
Response: 200
[
{
"id": "uuid",
"title": "Snake Game Help",
"model_name": "qwen3.5:9b",
"message_count": 4,
"created_at": "2026-03-06T10:00:00",
"updated_at": "2026-03-06T10:05:00"
}
]POST /api/conversations
Request Body:
{
"title": "New Chat (optional)",
"model_name": "optional",
"system_prompt": "optional"
}Response: 201 — returns ConversationResponse
GET /api/conversations/{conv_id}
Response: 200
{
"id": "uuid",
"title": "Snake Game Help",
"model_name": "qwen3.5:9b",
"system_prompt": null,
"created_at": "2026-03-06T10:00:00",
"updated_at": "2026-03-06T10:05:00",
"messages": [
{
"id": "msg-uuid",
"role": "user",
"content": "Help me build a snake game",
"thinking": null,
"image_base64": null,
"tokens_used": 0,
"tokens_per_sec": 0,
"thinking_duration": 0,
"created_at": "2026-03-06T10:00:00"
}
]
}PUT /api/conversations/{conv_id}
Request Body:
{
"title": "New title (optional)",
"system_prompt": "optional"
}Response: 200 — returns ConversationResponse
DELETE /api/conversations/{conv_id}
Response: 204 No Content
GET /api/conversations/{conv_id}/messages
Response: 200 — returns MessageResponse[]
PUT /api/conversations/{conv_id}/messages/{msg_id}
Request Body:
{
"content": "Updated message text"
}Response: 200 — returns MessageResponse
DELETE /api/conversations/{conv_id}/messages/{msg_id}
Deletes this message and everything after it. Creates a snapshot for undo.
Response: 200
{
"snapshot_id": "uuid",
"deleted_count": 3,
"conversation_deleted": false
}POST /api/conversations/restore/{snapshot_id}
Restores previously deleted messages.
Response: 200
{
"conversation_id": "uuid",
"restored_count": 3
}GET /api/models
Response: 200
[
{
"name": "qwen3.5:9b",
"size": "5.5 GB",
"modified_at": "2026-03-01T12:00:00"
}
]POST /api/models/pull
Request Body:
{
"name": "llama3.2"
}GET /api/user
Response: 200
{
"id": "default",
"name": "User",
"default_model": "qwen3.5:9b",
"system_prompt": null
}PUT /api/user
Request Body:
{
"name": "optional",
"default_model": "optional",
"system_prompt": "optional"
}GET /api/health
Response: 200
{
"status": "healthy",
"ollama": true,
"database": true
}All errors follow this format:
{
"detail": "Conversation not found"
}| Status Code | Meaning |
|---|---|
400 |
Bad request |
404 |
Resource not found |
422 |
Validation error (Pydantic) |
500 |
Internal server error |