← Voltar a studyAI — Documentação do Projeto

🔌 Especificação da API

studyAI — Documentação do Projeto

Apresentação

Especificação da API — studyAI

API REST do backend FastAPI. Autenticação via Bearer token (JWT).


Índice

  1. Convenções
  2. Auth
  3. Projects
  4. Files
  5. Documents
  6. Chat
  7. Generate
  8. Evaluate
  9. Index
  10. Erros

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" }
  }
}
HTTPCódigoQuando
400VALIDATION_ERRORDados inválidos
401UNAUTHORIZEDToken inválido ou expirado
403FORBIDDENSem permissão para o recurso
404NOT_FOUNDRecurso não existe
429RATE_LIMITEDRate limit excedido
500INTERNAL_ERRORErro do servidor

Zona de prática

Sem perguntas. Clica em Editar para adicionar.