Apresentação
Especificação da API — studyAI
API REST do backend FastAPI. Autenticação via Bearer token (JWT).
Índice
Convenções
- Base URL:
https://api.studyai.example/v1(dev:http://localhost:8000/v1) - Content-Type:
application/json - Auth:
Authorization: Bearer <access_token> - Paginação:
?page=1&limit=20(default: limit=20, max=100) - IDs: UUID v4
Auth
Fluxo de autenticação
A carregar diagrama…
POST /auth/register
Registo de novo utilizador.
Request:
{
"email": "user@example.com",
"password": "securePassword123",
"name": "João Silva"
}
Response 201:
{
"user": {
"id": "uuid",
"email": "user@example.com",
"name": "João Silva",
"created_at": "2025-03-20T10:00:00Z"
},
"access_token": "eyJ...",
"refresh_token": "eyJ...",
"expires_in": 900
}
Erros: 400 (email já existe, password fraca)
POST /auth/login
Login.
Request:
{
"email": "user@example.com",
"password": "securePassword123"
}
Response 200:
{
"user": { "id": "uuid", "email": "...", "name": "..." },
"access_token": "eyJ...",
"refresh_token": "eyJ...",
"expires_in": 900
}
Erros: 401 (credenciais inválidas)
POST /auth/refresh
Renovar access token.
Request:
{
"refresh_token": "eyJ..."
}
Response 200:
{
"access_token": "eyJ...",
"expires_in": 900
}
GET /auth/me
Utilizador autenticado. Requer Bearer token.
Response 200:
{
"id": "uuid",
"email": "user@example.com",
"name": "João Silva",
"created_at": "2025-03-20T10:00:00Z"
}
Erros: 401 (não autenticado)
Projects
GET /projects
Listar projetos do utilizador. Paginado.
Query: ?page=1&limit=20
Response 200:
{
"items": [
{
"id": "uuid",
"title": "Endoscopias",
"created_at": "2025-03-20T10:00:00Z",
"updated_at": "2025-03-20T10:00:00Z",
"file_count": 5,
"document_count": 3
}
],
"total": 10,
"page": 1,
"limit": 20
}
POST /projects
Criar projeto.
Request:
{
"title": "Endoscopias"
}
Response 201:
{
"id": "uuid",
"title": "Endoscopias",
"created_at": "2025-03-20T10:00:00Z",
"updated_at": "2025-03-20T10:00:00Z"
}
GET /projects/:id
Obter projeto (com contagens).
Response 200:
{
"id": "uuid",
"title": "Endoscopias",
"created_at": "2025-03-20T10:00:00Z",
"updated_at": "2025-03-20T10:00:00Z",
"files": [...],
"documents": [...]
}
Erros: 404 (projeto não existe ou não pertence ao user)
PUT /projects/:id
Atualizar projeto.
Request:
{
"title": "Endoscopias - Avançado"
}
Response 200: Projeto atualizado.
DELETE /projects/:id
Apagar projeto (e todos os dados associados).
Response 204: No content.
Files
POST /projects/:id/files
Upload de ficheiro. multipart/form-data.
Request:
file: ficheiro (PDF, TXT, MD)filename: (opcional) nome a guardar
Response 201:
{
"id": "uuid",
"filename": "artigo.pdf",
"mime_type": "application/pdf",
"size": 1024000,
"status": "pending",
"created_at": "2025-03-20T10:00:00Z"
}
Nota: status passa a processed após ingestão. error se falhar.
GET /projects/:id/files
Listar ficheiros do projeto.
Response 200:
{
"items": [
{
"id": "uuid",
"filename": "artigo.pdf",
"mime_type": "application/pdf",
"size": 1024000,
"status": "processed",
"created_at": "2025-03-20T10:00:00Z"
}
]
}
DELETE /projects/:id/files/:fileId
Apagar ficheiro. Remove do storage e do índice.
Response 204: No content.
Documents
GET /projects/:id/documents
Listar documentos gerados.
Response 200:
{
"items": [
{
"id": "uuid",
"title": "Técnicas de colonoscopia",
"created_at": "2025-03-20T10:00:00Z",
"updated_at": "2025-03-20T10:00:00Z",
"question_count": 12
}
]
}
POST /projects/:id/documents
Criar documento apenas manualmente (título + conteúdo). Não dispara geração.
Para geração assíncrona: usar POST /projects/:id/generate.
Request:
{
"title": "Meus apontamentos",
"content": "# Conteúdo..."
}
Response 201:
{
"id": "uuid",
"title": "Meus apontamentos",
"content": "# Conteúdo...",
"status": "ready",
"created_at": "2025-03-20T10:00:00Z"
}
GET /projects/:id/documents/:docId
Obter documento (com perguntas).
Response 200:
{
"id": "uuid",
"title": "Técnicas de colonoscopia",
"content": "# Técnicas...",
"structure": { "sections": [...] },
"questions": [
{
"id": "uuid",
"question": "O que é uma colonoscopia?",
"solution": "...",
"spoken": "...",
"type": "recall"
}
],
"created_at": "2025-03-20T10:00:00Z",
"updated_at": "2025-03-20T10:00:00Z"
}
PUT /projects/:id/documents/:docId
Atualizar documento (ex: edição manual).
Request:
{
"title": "Técnicas de colonoscopia (revisado)",
"content": "# Conteúdo atualizado..."
}
Response 200: Documento atualizado.
Nota: Re-indexar após edição para atualizar RAG.
DELETE /projects/:id/documents/:docId
Apagar documento.
Response 204: No content.
Chat
POST /projects/:id/chat
Enviar mensagem ao sistema. Router decide o intent e encaminha.
Request:
{
"message": "O que é uma colonoscopia?",
"session_id": "uuid",
"history": [
{ "role": "user", "content": "..." },
{ "role": "assistant", "content": "..." }
]
}
session_id: Opcional. Se omitido, cria nova sessão. Se fornecido, persiste mensagens em chat_sessions/chat_messages. Frontend pode passar do último session_id para continuar conversa.
Response 200 (streaming): Content-Type: text/event-stream
data: {"type": "chunk", "content": "A colonoscopia"}
data: {"type": "chunk", "content": " é um exame"}
...
data: {"type": "done", "citations": [...]}
Response 200 (não-streaming):
{
"content": "A colonoscopia é um exame endoscópico...",
"citations": [
{ "source": "artigo.pdf", "chunk_id": "...", "excerpt": "..." }
],
"intent": "qa"
}
Generate
POST /projects/:id/generate
Disparar geração de material sobre um tópico. Assíncrono.
Request:
{
"topic": "Técnicas de colonoscopia"
}
Response 202:
{
"job_id": "uuid",
"status": "pending",
"message": "Geração iniciada. Consulte o documento quando estiver pronto."
}
Polling: GET /projects/:id/jobs/:jobId para status e progresso.
GET /projects/:id/jobs/:jobId
Obter estado de um job assíncrono.
Response 200:
{
"id": "uuid",
"type": "generate_content",
"status": "running",
"progress": 45,
"message": "A escrever secção 2 de 5...",
"result": null,
"error": null,
"created_at": "2025-03-20T10:00:00Z",
"updated_at": "2025-03-20T10:02:00Z"
}
status: pending | running | completed | failed
progress: 0–100. Em generate_content: 0=planning, 20=secção 1, 40=secção 2, ..., 80=questions, 100=done.
Streaming progress (alternativa): GET /projects/:id/jobs/:jobId/stream — SSE com eventos {"progress": 20, "message": "A escrever secção 1..."}.
Evaluate
POST /projects/:id/evaluate
Avaliar resposta do utilizador a uma pergunta.
Request:
{
"question_id": "uuid",
"user_answer": "A colonoscopia é um exame que permite visualizar o cólon..."
}
Response 200:
{
"score": 85,
"feedback": "Boa resposta. Poderia mencionar também a preparação intestinal.",
"corrections": ["Adicionar: preparação com laxantes 24h antes"],
"saved": true
}
Index
POST /projects/:id/index
Reindexar projeto. Útil após novos ficheiros ou edição de documentos.
Request: (vazio)
Response 200:
{
"status": "indexing",
"message": "Indexação iniciada. Pode levar alguns minutos."
}
Response 200 (já indexado):
{
"status": "ready",
"chunk_count": 150
}
Erros
Formato padrão:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Email já registado",
"details": { "field": "email" }
}
}
| HTTP | Código | Quando |
|---|---|---|
| 400 | VALIDATION_ERROR | Dados inválidos |
| 401 | UNAUTHORIZED | Token inválido ou expirado |
| 403 | FORBIDDEN | Sem permissão para o recurso |
| 404 | NOT_FOUND | Recurso não existe |
| 429 | RATE_LIMITED | Rate limit excedido |
| 500 | INTERNAL_ERROR | Erro do servidor |
Zona de prática
Sem perguntas. Clica em Editar para adicionar.