← Volver al índice | App Móvil | Web SPA
Especificación — API REST y WebSocket¶
Tipo: Especificación Funcional — Contrato de API
Audiencia: Equipo de desarrollo backend y frontend, integradores
Fecha: 20 de marzo de 2026
Relacionado con: Arquitectura del Sistema | Modelo de Datos
1. Información General¶
| Parámetro | Valor |
|---|---|
| Base URL | http://localhost:8080/api/v1 |
| Formato | JSON (application/json) |
| Autenticación | Bearer Token (JWT via Entra ID) |
| Versionado | Path-based (/api/v1/, /api/v2/) |
| Documentación | OpenAPI 3.0 en /api/docs |
| Rate limiting | 100 req/min por usuario (configurable) |
2. Autenticación y Seguridad¶
2.1 Flujo de Autenticación¶
sequenceDiagram
participant APP as App / Web
participant ENTRA as Microsoft Entra ID
participant API6 as Backend API
APP->>ENTRA: Authorization Code + PKCE
ENTRA-->>APP: ID Token + Access Token
APP->>API6: GET /api/v1/... + Bearer Token
API6->>ENTRA: Valida token (JWKS)
ENTRA-->>API6: Token válido + claims
API6-->>APP: Respuesta 200
2.2 Headers Requeridos¶
Authorization: Bearer <jwt-token>
Content-Type: application/json
Accept: application/json
X-Request-Id: <uuid>
2.3 Roles y Permisos¶
| Rol | Descripción | Permisos |
|---|---|---|
INVESTIGADOR |
Investigador de campo | CRUD sobre sus muestras, consultas IA |
RESPONSABLE |
Responsable de departamento | Todo del investigador + ver muestras del dpto. |
ADMIN |
Administrador del sistema | Acceso completo |
API_CLIENT |
Cliente externo (F3) | Solo lectura via API pública |
3. Endpoints REST¶
3.1 Muestras (/samples)¶
POST /api/v1/samples — Crear muestra con identificación IA¶
Request (multipart/form-data):
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
image |
File | Sí | Imagen de la muestra (JPEG/PNG) |
codigo_interno |
String | Sí | Código IEO (ej: IEOMA-CFM-0042) |
latitud |
Float | Sí | Coordenada GPS |
longitud |
Float | Sí | Coordenada GPS |
fecha_captura |
Date | No | Por defecto: hoy |
talla_cm |
Float | No | Talla en centímetros |
peso_g |
Float | No | Peso en gramos |
sexo |
String | No | M, F, INDETERMINADO |
metodo_captura |
String | No | ARRASTRE, PALANGRE, NASAS, OTRO |
campana_id |
UUID | No | Campaña de origen |
notas |
String | No | Notas del investigador |
Response (201 Created):
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"codigo_interno": "IEOMA-CFM-0042",
"imagen_url": "https://minio:9000/ieo-images/550e8400.jpg",
"identificación": {
"especie_sugerida": {
"nombre_cientifico": "Mullus surmuletus",
"nombre_comun": "Salmonete de roca",
"confianza": 0.92
},
"edad_estimada": 3,
"sexo_estimado": "F",
"top_k_similares": [
{ "muestra_id": "uuid-1", "score": 0.95, "thumbnail": "url" },
{ "muestra_id": "uuid-2", "score": 0.91, "thumbnail": "url" }
],
"modelo_utilizado": "qwen25-vl-7b",
"confirmada": false
},
"created_at": "2026-03-20T10:30:00Z"
}
GET /api/v1/samples — Listar muestras¶
Query Parameters:
| Parámetro | Tipo | Descripción |
|---|---|---|
page |
Int | Página (default: 0) |
size |
Int | Tamaño de página (default: 20, max: 100) |
especie_id |
UUID | Filtrar por especie |
campana_id |
UUID | Filtrar por campaña |
coleccion_id |
UUID | Filtrar por colección |
fecha_desde |
Date | Fecha mínima |
fecha_hasta |
Date | Fecha máxima |
confirmada |
Boolean | Solo identificaciones confirmadas/pendientes |
sort |
String | Campo de ordenación (default: created_at,desc) |
Response (200 OK): Paginado estándar Spring.
GET /api/v1/samples/{id} — Detalle de muestra¶
Response (200 OK): Objeto muestra completo con parámetros biológicos, imágenes, identificación IA e historial.
PATCH /api/v1/samples/{id}/confirm — Confirmar identificación IA¶
Request:
{
"confirmada": true,
"especie_corregida_id": null,
"notas_revision": "Identificación correcta"
}
3.2 Búsqueda Semántica (/search)¶
POST /api/v1/search — Búsqueda en lenguaje natural¶
Request:
{
"query": "Otolitos de sardina del Mediterráneo capturados entre 2015 y 2020",
"limit": 20,
"include_images": true
}
Response (200 OK):
{
"results": [
{
"muestra_id": "uuid",
"relevance_score": 0.89,
"especie": "Sardina pilchardus",
"fecha_captura": "2017-06-15",
"thumbnail": "url",
"snippet": "Otolito de sardina, campaña ECOMED 2017..."
}
],
"total": 47,
"query_interpreted": "Búsqueda: otolitos + sardina + Mediterráneo + 2015-2020"
}
POST /api/v1/search/by-image — Búsqueda por similitud visual¶
Request (multipart/form-data):
| Campo | Tipo | Descripción |
|---|---|---|
image |
File | Imagen de referencia |
limit |
Int | Número de resultados (default: 10) |
min_score |
Float | Score mínimo de similitud (default: 0.5) |
3.3 Especies (/species)¶
GET /api/v1/species — Listar especies¶
Query Parameters: search (texto libre), familia, clase, page, size.
GET /api/v1/species/{id} — Detalle de especie con taxonomía completa.¶
3.4 Campañas (/campaigns)¶
GET /api/v1/campaigns — Listar campañas¶
Query Parameters: year, type, zona_id, page, size.
GET /api/v1/campaigns/{id} — Detalle de campaña con estadísticas.¶
3.5 Sistema (/system)¶
GET /api/v1/system/health — Estado de salud¶
{
"status": "UP",
"services": {
"database": "UP",
"chromadb": "UP",
"ollama": "UP",
"redis": "UP",
"minio": "UP"
},
"version": "1.0.0",
"uptime": "3d 12h 45m"
}
4. WebSocket — Vídeo en Tiempo Real¶
4.1 Conexión¶
ws://localhost:8080/ws/realtime
Header: Authorization: Bearer <jwt-token>
4.2 Protocolo de Mensajes¶
sequenceDiagram
participant APP2 as App / Web
participant WS as WebSocket Server
participant PK as Dapr Sidecar
APP2->>WS: CONNECT /ws/realtime
WS->>PK: Crea RealTimeDetectionActor
loop Cada N frames
APP2->>WS: FRAME - base64 image
WS->>PK: processFrame
PK-->>WS: DETECTION result
WS-->>APP2: DETECTION JSON
end
APP2->>WS: CAPTURE - frame actual
WS->>PK: captureAndProcess
PK-->>WS: SAMPLE_CREATED result
WS-->>APP2: SAMPLE_CREATED JSON
APP2->>WS: DISCONNECT
WS->>PK: Destruye actor
4.3 Mensajes del Cliente¶
| Tipo | Payload | Descripción |
|---|---|---|
FRAME |
{ "image": "base64...", "timestamp": 1234567890 } |
Frame de vídeo para clasificación |
CAPTURE |
{ "metadata": { ... } } |
Capturar frame actual como muestra |
CONFIG |
{ "fps_target": 15, "min_confidence": 0.6 } |
Configurar sesión |
4.4 Mensajes del Servidor¶
| Tipo | Payload | Descripción |
|---|---|---|
DETECTION |
{ "species": "...", "confidence": 0.85, "bbox": [x,y,w,h] } |
Resultado de detección por frame |
SAMPLE_CREATED |
{ "sample_id": "uuid", "identificacion": { ... } } |
Muestra registrada exitosamente |
ERROR |
{ "code": "...", "message": "..." } |
Error en procesamiento |
5. Códigos de Error¶
| Código HTTP | Código Interno | Descripción |
|---|---|---|
| 400 | INVALID_REQUEST |
Parámetros inválidos o faltantes |
| 401 | UNAUTHORIZED |
Token ausente o expirado |
| 403 | FORBIDDEN |
Sin permisos para este recurso |
| 404 | NOT_FOUND |
Recurso no encontrado |
| 409 | DUPLICATE |
Código interno duplicado |
| 413 | IMAGE_TOO_LARGE |
Imagen excede 20MB |
| 422 | UNSUPPORTED_FORMAT |
Formato de imagen no soportado |
| 429 | RATE_LIMITED |
Excedido el límite de peticiones |
| 500 | INTERNAL_ERROR |
Error interno del servidor |
| 503 | AI_UNAVAILABLE |
Motor IA no disponible (Ollama down) |
Formato de error estándar:
{
"error": {
"code": "INVALID_REQUEST",
"message": "El campo 'codigo_interno' es obligatorio",
"timestamp": "2026-03-20T10:30:00Z",
"request_id": "uuid"
}
}
Documentos Relacionados¶
| Nivel | Documento | Descripción |
|---|---|---|
| Arquitectura | Arquitectura del Sistema | Backend Quarkus + Dapr |
| Arquitectura | Modelo de Datos | Schema de las entidades |
| Especificación | App Móvil | Cliente principal de esta API |
| Especificación | Web SPA | Cliente web de esta API |
| Infraestructura | Docker Compose | Servicio api |