Saltar a contenido

Manual de Uso — Sherlock API REST

Framework: FastAPI 0.135+ Entry point: uvicorn sherlock_docs.interfaces.api.main:app Puerto por defecto: 8000 Swagger UI: http://localhost:8000/docs OpenAPI spec: http://localhost:8000/openapi.json Requisito: pip install -e ".[api]" Endpoints: 22 (7 Foundation S24 + 15 Completa S25) Autenticacion: JWT Bearer (HS256)


Inicio Rapido

Arrancar la API

# Desarrollo (auth deshabilitado, auto-reload)
SHERLOCK_AUTH_ENABLED=0 uvicorn sherlock_docs.interfaces.api.main:app --port 8000 --reload

# Produccion
SHERLOCK_JWT_SECRET=clave-segura-produccion \
SHERLOCK_DATABASE_PATH=data/database/sherlock.db \
SHERLOCK_AUTH_ENABLED=1 \
uvicorn sherlock_docs.interfaces.api.main:app --host 0.0.0.0 --port 8000

Verificar que funciona

# Health check (no requiere autenticacion)
curl http://localhost:8000/api/v1/health
{
  "status": "healthy",
  "version": "1.0.0",
  "components": [
    {"name": "database", "status": "ok", "detail": "42 documents"},
    {"name": "ocr", "status": "ok", "detail": null},
    {"name": "ner", "status": "ok", "detail": null},
    {"name": "dedup", "status": "ok", "detail": "42 indexed"}
  ]
}

Configuracion

Todas las variables de entorno usan el prefijo SHERLOCK_:

Variable Descripcion Default
SHERLOCK_DATABASE_PATH Ruta a la base de datos SQLite data/database/sherlock.db
SHERLOCK_JWT_SECRET Clave secreta para firmar tokens JWT dev-secret-change-in-production
SHERLOCK_JWT_ALGORITHM Algoritmo JWT HS256
SHERLOCK_JWT_EXPIRY_HOURS Horas de validez del token 8
SHERLOCK_JWT_MAX_REFRESHES Maximo de refreshes por sesion 5
SHERLOCK_MAX_UPLOAD_SIZE_MB Tamano maximo de archivo (MB) 50
SHERLOCK_AUTH_ENABLED Activar autenticacion (true/false) true
SHERLOCK_AUTH_CONFIG_PATH Ruta al YAML con credenciales bcrypt .streamlit/auth_config.yaml
SHERLOCK_CORS_ORIGINS Origenes permitidos CORS (JSON array) ["http://localhost:3000","http://localhost:8501"]
SHERLOCK_API_PREFIX Prefijo de ruta para todos los endpoints /api/v1
SHERLOCK_PREWARM_MODELS Pre-cargar modelos ML al iniciar false

Tambien se puede usar un archivo .env en la raiz del proyecto:

SHERLOCK_JWT_SECRET=mi-clave-secreta
SHERLOCK_AUTH_ENABLED=1
SHERLOCK_PREWARM_MODELS=1

Autenticacion (JWT Bearer)

La API usa tokens JWT con el esquema Bearer. Todos los endpoints excepto /health y /auth/login requieren autenticacion.

Obtener token

curl -X POST http://localhost:8000/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username": "admin", "password": "mi-password"}'
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "bearer",
  "expires_in": 28800
}

Usar token en peticiones

TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

curl http://localhost:8000/api/v1/documents \
  -H "Authorization: Bearer $TOKEN"

Renovar token

curl -X POST http://localhost:8000/api/v1/auth/refresh \
  -H "Authorization: Bearer $TOKEN"

Limite: Maximo 5 refreshes por sesion (configurable via SHERLOCK_JWT_MAX_REFRESHES). Despues se requiere nuevo login.

Ver usuario actual

curl http://localhost:8000/api/v1/auth/me \
  -H "Authorization: Bearer $TOKEN"
{
  "username": "admin",
  "role": "operator"
}

Credenciales: La API reutiliza las mismas credenciales bcrypt de la GUI Streamlit. Se leen del archivo .streamlit/auth_config.yaml o de la variable de entorno AUTH_USERS (JSON con hashes bcrypt).


Formato de Respuestas

Respuesta exitosa

{"data": { ... }}

Para listas paginadas:

{"data": [ ... ], "meta": {"total": 142, "page": 1, "per_page": 20, "total_pages": 8}}

Respuesta de error

{"error": {"code": "error_404", "message": "Document 'xyz' no encontrado"}}

Codigos HTTP

Codigo Significado Cuando
200 OK Operacion exitosa
201 Created Documento creado (manual, import)
202 Accepted OCR encolado (process)
204 No Content Eliminacion exitosa
401 Unauthorized Token invalido o faltante
404 Not Found Documento/task no existe
413 Payload Too Large Archivo excede limite de tamano
422 Unprocessable Entity Datos de request invalidos
500 Internal Server Error Error inesperado del servidor
503 Service Unavailable ServiceContainer no inicializado

Endpoints (22 totales)

1. GET /api/v1/health — Estado del sistema

Verifica la disponibilidad de todos los componentes: base de datos, OCR, NER y deteccion de duplicados.

Autenticacion: No requerida

curl http://localhost:8000/api/v1/health
Campo Tipo Descripcion
status string healthy, degraded o unhealthy
version string Version de la API
components array Lista de componentes con su estado

Uso recomendado: Configurar como health check en Docker, EasyPanel, o balanceador de carga.


2. POST /api/v1/auth/login — Iniciar sesion

Autenticacion: No requerida Rate limit: 5 intentos por minuto por IP (S26 H-02)

curl -X POST http://localhost:8000/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username": "admin", "password": "password123"}'
Parametro Tipo Requerido Descripcion
username string Si (min 1 char) Nombre de usuario
password string Si (min 1 char) Contrasena en texto plano

Respuesta exitosa (200):

{
  "access_token": "eyJ...",
  "token_type": "bearer",
  "expires_in": 28800
}

Errores: 401 (credenciales invalidas), 422 (campos vacios), 429 (rate limit excedido)


3. POST /api/v1/auth/refresh — Renovar token

Genera un nuevo JWT con la misma identidad. Maximo 5 refreshes por sesion.

curl -X POST http://localhost:8000/api/v1/auth/refresh \
  -H "Authorization: Bearer $TOKEN"

Error (401): Limite de refreshes alcanzado


4. GET /api/v1/auth/me — Usuario actual

curl http://localhost:8000/api/v1/auth/me \
  -H "Authorization: Bearer $TOKEN"
{"username": "admin", "role": "operator"}

5. GET /api/v1/documents — Listar documentos

Lista paginada de documentos ordenados por fecha de creacion (mas recientes primero).

curl "http://localhost:8000/api/v1/documents?page=1&per_page=20" \
  -H "Authorization: Bearer $TOKEN"
Parametro Tipo Default Descripcion
page int 1 Numero de pagina (>=1)
per_page int 20 Documentos por pagina (1-100)

Respuesta (200):

{
  "data": [
    {
      "id": "abc-123",
      "file_name": "tutela_2024.pdf",
      "document_type": "tutela",
      "status": "processed",
      "demandante": "Juan Garcia",
      "demandado": "EPS Sanitas",
      "radicado": "05001-02-03-000-2024-00001-00",
      "created_at": "2024-03-20 14:30:00"
    }
  ],
  "meta": {"total": 142, "page": 1, "per_page": 20, "total_pages": 8}
}

Equivalente CLI: sherlock list --limit 20


6. GET /api/v1/documents/{id} — Detalle de documento

Retorna toda la informacion de un documento incluyendo entidades extraidas.

curl http://localhost:8000/api/v1/documents/abc-123 \
  -H "Authorization: Bearer $TOKEN"

Respuesta (200):

{
  "data": {
    "id": "abc-123",
    "file_name": "tutela_2024.pdf",
    "document_type": "tutela",
    "status": "processed",
    "demandante": "Juan Garcia Rodriguez",
    "demandado": "EPS Sanitas",
    "radicado": "05001-02-03-000-2024-00001-00",
    "juzgado": "Juzgado Primero Civil Municipal de Bello",
    "cedula": "1234567890",
    "correo": "juan.garcia@email.com",
    "ocr_confidence": 0.87,
    "is_manual_entry": false,
    "created_at": "2024-03-20 14:30:00"
  }
}

Error (404): Documento no encontrado

Equivalente CLI: sherlock detail abc-123


7. GET /api/v1/documents/stats/dashboard — Estadisticas

Retorna las estadisticas del dashboard (totales, por tipo, por estado).

curl http://localhost:8000/api/v1/documents/stats/dashboard \
  -H "Authorization: Bearer $TOKEN"

Equivalente CLI: sherlock stats --format json


8. POST /api/v1/documents/process — Procesar PDF (async)

Sube un PDF y encola procesamiento asincrono OCR+NER+dedup. Devuelve 202 con task_id para polling.

curl -X POST http://localhost:8000/api/v1/documents/process \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@tutela_001.pdf" \
  -F "doc_type=tutela"
Parametro Tipo Requerido Default Descripcion
file UploadFile Si Archivo PDF a procesar
doc_type string No tutela Tipo de documento

Respuesta (202 Accepted):

{
  "task_id": "abc123def456",
  "status": "pending",
  "message": "Procesamiento encolado"
}

Errores: 413 (archivo > 50MB), 422 (no es PDF)

Equivalente CLI: sherlock process archivo.pdf


9. GET /api/v1/tasks/{task_id} — Estado de tarea async

Consulta el estado de una tarea asincrona (polling para procesamiento OCR).

curl http://localhost:8000/api/v1/tasks/abc123def456 \
  -H "Authorization: Bearer $TOKEN"

Respuesta (200) — Tarea completada:

{
  "task_id": "abc123def456",
  "status": "completed",
  "progress": 1.0,
  "result": {
    "id": "doc-uuid",
    "file_name": "tutela_001.pdf",
    "demandante": "Juan Perez",
    "demandado": "EPS Salud",
    "ocr_confidence": 0.87,
    "processing_time_ms": 45000
  },
  "error": null,
  "created_at": "2026-03-25 10:00:00+00:00",
  "completed_at": "2026-03-25 10:00:45+00:00"
}

Valores de status: pending, processing, completed, failed

Error (404): Task no encontrado o expirado


10. PUT /api/v1/documents/{id}/validate — Guardar correcciones NER

Guarda las correcciones de entidades validadas por el operador humano.

curl -X PUT http://localhost:8000/api/v1/documents/abc-123/validate \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "validated_fields": {"demandante": "Juan Alberto Perez", "cedula": "1234567890"},
    "original_fields": {"demandante": "Juan A. Perez", "cedula": "123456789O"},
    "corrected_by": "operator"
  }'

Respuesta (200):

{
  "data": {
    "corrections_saved": 2,
    "document_id": "abc-123",
    "corrected_by": "admin"
  }
}

Errores: 404 (documento no existe), 500 (error guardando)

Nota: Los campos string se sanitizan contra XSS automaticamente.

Equivalente CLI: sherlock update <doc_id> <campo> <valor>


11. POST /api/v1/documents/{id}/recurring — Entidades recurrentes

Verifica si las entidades (cedula, correo, demandante) aparecen en otros documentos.

curl -X POST http://localhost:8000/api/v1/documents/abc-123/recurring \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"cedula": "1234567890", "correo": "juan@mail.com", "demandante": "Juan Perez"}'

Respuesta (200):

{
  "data": {
    "cedula_matches": [
      {"document_id": "other-doc", "file_name": "tutela_005.pdf", "demandante": "Juan Perez"}
    ],
    "correo_matches": [],
    "demandante_matches": [],
    "total_matches": 1
  }
}

Equivalente CLI: sherlock find --correo <correo> / sherlock find --accionante <nombre>


12. POST /api/v1/documents/manual — Entrada manual

Crea un documento manualmente sin procesamiento OCR.

curl -X POST http://localhost:8000/api/v1/documents/manual \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "numero_acta": "2026-0001",
    "demandante": "Maria Lopez",
    "demandado": "EPS Salud Total",
    "doc_type": "tutela",
    "juzgado": "Juzgado 1 Civil Municipal de Bello",
    "cedula": "9876543210"
  }'
Campo Tipo Requerido Descripcion
numero_acta string Si Numero de acta de reparto
demandante string Si Nombre del accionante
demandado string Si Nombre del demandado
doc_type string No Tipo de documento (default: tutela)
radicado string No Numero de radicado
juzgado string No Juzgado asignado
cedula string No Cedula del demandante
correo string No Correo del demandante
direccion_demandante string No Direccion del demandante
direccion_demandado string No Direccion del demandado
observaciones string No Notas u observaciones
etiquetas string No Etiquetas separadas por coma

Respuesta (201 Created):

{
  "data": {
    "document_id": "new-doc-uuid",
    "numero_acta": "2026-0001",
    "status": "manual_entry",
    "is_manual_entry": true
  }
}

Errores: 422 (campos requeridos faltantes)

Nota: Todos los campos string se sanitizan contra XSS automaticamente.


13. GET /api/v1/documents/search — Busqueda full-text

Busqueda full-text usando SQLite FTS5 con paginacion.

curl "http://localhost:8000/api/v1/documents/search?q=tutela+salud&page=1&page_size=50" \
  -H "Authorization: Bearer $TOKEN"
Parametro Tipo Default Descripcion
q string "" Termino de busqueda FTS5
page int 1 Numero de pagina
page_size int 50 Items por pagina (1-200)
sort_by string Relevancia Campo de ordenamiento

Respuesta (200):

{
  "data": {
    "documents": [
      {
        "id": "doc-uuid",
        "file_name": "tutela_001.pdf",
        "demandante": "Juan Perez",
        "demandado": "EPS Salud",
        "document_type": "tutela",
        "status": "validated"
      }
    ],
    "total": 150,
    "page": 1,
    "page_size": 50
  }
}

Equivalente CLI: sherlock search "termino" --limit N


14. POST /api/v1/documents/{id}/duplicates — Detectar duplicados

Detecta documentos duplicados usando el ensemble hibrido (MinHash + TF-IDF + Entity matcher).

curl -X POST http://localhost:8000/api/v1/documents/abc-123/duplicates \
  -H "Authorization: Bearer $TOKEN"

Respuesta (200):

{
  "data": {
    "candidates": [
      {
        "document_id": "other-doc",
        "similarity": 0.85,
        "file_name": "tutela_005.pdf",
        "details": {
          "minhash_score": 0.80,
          "tfidf_score": 0.88,
          "entity_score": 0.90,
          "ensemble_score": 0.85
        }
      }
    ],
    "total": 1
  }
}

Errores: 404 (documento no encontrado), 500 (error de deteccion)


15. POST /api/v1/duplicates/verify — Verificar texto contra corpus

Verifica un fragmento de texto libre contra el corpus existente.

curl -X POST http://localhost:8000/api/v1/duplicates/verify \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "El accionante interpone accion de tutela contra EPS...",
    "document_type": "tutela"
  }'
Campo Tipo Requerido Default Descripcion
text string Si Texto a verificar
document_type string No tutela Tipo para umbrales

Nota (S26 D-08): El campo threshold fue eliminado. Los umbrales se determinan internamente segun document_type y son configurables via PUT /config/dedup.

Respuesta (200):

{
  "data": {
    "candidates": [
      {
        "document_id": "existing-doc",
        "similarity": 0.78,
        "file_name": "tutela_032.pdf"
      }
    ],
    "total": 1
  }
}

Errores: 422 (texto vacio)


16. POST /api/v1/import/excel — Importar desde Excel

Importa documentos desde archivo Excel (.xlsx/.xls).

curl -X POST http://localhost:8000/api/v1/import/excel \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@documentos.xlsx" \
  -F "update_existing=false"
Parametro Tipo Requerido Default Descripcion
file UploadFile Si Archivo Excel
update_existing bool No false Actualizar existentes

Respuesta (201 Created):

{
  "data": {
    "imported": 25,
    "updated": 3,
    "skipped": 2,
    "errors": [],
    "total_rows": 30
  }
}

Errores: 413 (> 50MB), 422 (no es Excel)

Equivalente CLI: sherlock import archivo.xlsx


17. GET /api/v1/export/excel — Exportar a Excel

Exporta documentos a archivo Excel (descarga directa .xlsx).

curl "http://localhost:8000/api/v1/export/excel?filename=indice_marzo&date_from=2026-03-01&date_to=2026-03-25" \
  -H "Authorization: Bearer $TOKEN" \
  -o indice_marzo.xlsx
Parametro Tipo Default Descripcion
filename string indice_documentos Nombre del archivo (sin extension)
date_from string "" Fecha inicio filtro
date_to string "" Fecha fin filtro

Respuesta (200): Descarga directa del archivo .xlsx

Errores: 404 (sin documentos para exportar)

Equivalente CLI: sherlock export --filename nombre


18. GET /api/v1/config/dedup — Configuracion de duplicados

Obtiene la configuracion actual del ensemble de deteccion de duplicados.

curl http://localhost:8000/api/v1/config/dedup \
  -H "Authorization: Bearer $TOKEN"

Respuesta (200):

{
  "data": {
    "minhash_weight": 0.25,
    "tfidf_weight": 0.40,
    "entity_weight": 0.35,
    "tutela_threshold": 0.70,
    "habeas_threshold": 0.65,
    "default_threshold": 0.70
  }
}

19. PUT /api/v1/config/dedup — Actualizar config duplicados

Actualiza pesos y umbrales del ensemble. Los 3 pesos deben sumar 1.0.

Requiere rol admin (S26 H-01 RBAC)

curl -X PUT http://localhost:8000/api/v1/config/dedup \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "minhash_weight": 0.30,
    "tfidf_weight": 0.35,
    "entity_weight": 0.35
  }'

Errores: 403 (no es admin), 422 (pesos no suman 1.0)


20. GET /api/v1/config/ner — Configuracion NER

Obtiene pesos del extractor NER (ensemble de 5 extractores).

curl http://localhost:8000/api/v1/config/ner \
  -H "Authorization: Bearer $TOKEN"

Respuesta (200):

{
  "data": {
    "weights": {"marker": 1, "spacy": 2, "regex": 3, "address": 4, "contact": 5}
  }
}

21. DELETE /api/v1/documents/{id} — Eliminar documento

Elimina un documento por su ID.

Requiere rol admin (S26 H-01 RBAC)

curl -X DELETE http://localhost:8000/api/v1/documents/abc-123 \
  -H "Authorization: Bearer $TOKEN"

Respuesta (204): Sin cuerpo de respuesta

Errores: 403 (no es admin), 404 (documento no encontrado)

Equivalente CLI: sherlock delete <doc_id>


22. GET /api/v1/stats/processing — Metricas de procesamiento

Obtiene metricas de tiempos de procesamiento (promedios OCR, NER, dedup).

curl http://localhost:8000/api/v1/stats/processing \
  -H "Authorization: Bearer $TOKEN"

Respuesta (200):

{
  "data": {
    "total_documents": 150,
    "total_processed": 120,
    "total_manual": 30,
    "avg_processing_time_ms": 35000,
    "documents_today": 5,
    "documents_this_week": 25
  }
}

Equivalente CLI: sherlock stats / sherlock stats-daily --days N


Tabla Resumen — 22 Endpoints

# Metodo Endpoint Auth Sprint Descripcion
1 GET /health No S24 Estado del sistema
2 POST /auth/login No S24 Iniciar sesion
3 POST /auth/refresh Si S24 Renovar token (max 5)
4 GET /auth/me Si S24 Usuario actual
5 GET /documents Si S24 Listar documentos (paginado)
6 GET /documents/{id} Si S24 Detalle de documento
7 GET /documents/stats/dashboard Si S24 Estadisticas dashboard
8 POST /documents/process Si S25 Procesar PDF async (202)
9 GET /tasks/{id} Si S25 Estado tarea async
10 PUT /documents/{id}/validate Si S25 Correcciones NER
11 POST /documents/{id}/recurring Si S25 Entidades recurrentes
12 POST /documents/manual Si S25 Entrada manual
13 GET /documents/search Si S25 Busqueda FTS5
14 POST /documents/{id}/duplicates Si S25 Detectar duplicados
15 POST /duplicates/verify Si S25 Verificar texto vs corpus
16 POST /import/excel Si S25 Importar Excel
17 GET /export/excel Si S25 Exportar Excel
18 GET /config/dedup Si S25 Config duplicados
19 PUT /config/dedup Si S25 Actualizar config dedup
20 GET /config/ner Si S25 Config NER
21 DELETE /documents/{id} Si S25 Eliminar documento
22 GET /stats/processing Si S25 Metricas procesamiento

Mapeo CLI a API

Accion CLI API
Ver estado del sistema sherlock health GET /health
Listar documentos sherlock list GET /documents
Ver detalle sherlock detail <id> GET /documents/{id}
Estadisticas sherlock stats GET /documents/stats/dashboard
Buscar sherlock search "texto" GET /documents/search?q=texto
Procesar PDF sherlock process archivo.pdf POST /documents/process
Validar campos sherlock update <id> <campo> <val> PUT /documents/{id}/validate
Entrada manual (GUI ManualEntry) POST /documents/manual
Buscar recurrentes sherlock find --correo <correo> POST /documents/{id}/recurring
Detectar duplicados (auto en process) POST /documents/{id}/duplicates
Verificar texto (no disponible) POST /duplicates/verify
Importar Excel sherlock import archivo.xlsx POST /import/excel
Exportar Excel sherlock export GET /export/excel
Config duplicados (no disponible) GET/PUT /config/dedup
Config NER (no disponible) GET /config/ner
Eliminar documento sherlock delete <id> DELETE /documents/{id}
Metricas procesamiento sherlock stats-daily GET /stats/processing

Seguridad

Aspecto Implementacion
Autenticacion JWT Bearer tokens (HS256, 8h expiry)
Credenciales Hashes bcrypt (reutiliza GOB-05)
Token refresh Max 5 por sesion (limitacion conocida H-07: client-side count)
RBAC require_admin en DELETE y PUT /config/dedup (S26 H-01)
Rate limiting 5 intentos/min por IP en login (S26 H-02)
Security headers X-Content-Type-Options, X-Frame-Options, X-XSS-Protection, Referrer-Policy, Permissions-Policy, HSTS en prod (S26 M-02)
CORS Restrictivo: solo GET/POST/PUT/DELETE/OPTIONS, headers Authorization/Content-Type/Accept (S26 M-03)
Swagger Deshabilitado en produccion (ENVIRONMENT=production) (S26 M-06)
File upload Max 50MB, extension validada, path traversal prevenido, filename sanitizado (S26 M-08)
Temp files Cleanup automatico en finally blocks
XSS Sanitizacion HTML + protocolos URI peligrosos + null bytes (S26 HALL-05)
SQL Injection Queries parametrizadas (nunca string format)
Error leaking Global exception handler, sin stack traces al cliente
Audit trail Todas las operaciones registradas via get_logger

Nota: En desarrollo, se puede desactivar la autenticacion con SHERLOCK_AUTH_ENABLED=0. Nunca usar esto en produccion.


Swagger UI

La documentacion interactiva esta disponible en http://localhost:8000/docs:

  • Prueba cualquier endpoint directamente desde el navegador
  • Haz click en "Authorize" para ingresar tu JWT token
  • Los schemas de request/response se muestran automaticamente
  • Descarga la especificacion OpenAPI en /openapi.json

La especificacion OpenAPI es consumible por NSwag/Refitter (C# .NET), openapi-generator, o Postman.


Arquitectura Interna

Cliente HTTP (.NET, curl, Postman)
    |
    v
FastAPI (uvicorn :8000)
    |
    +-- routes/health.py        -> Sin auth
    +-- routes/auth.py          -> JWT login/refresh/me
    +-- routes/search.py        -> Busqueda FTS5
    +-- routes/process.py       -> Upload + async OCR (TaskManager)
    +-- routes/manual.py        -> Entrada manual
    +-- routes/documents.py     -> List, detail, stats
    +-- routes/validate.py      -> Correcciones NER + recurring
    +-- routes/duplicates.py    -> Dedup detect + verify
    +-- routes/import_export.py -> Excel import/export
    +-- routes/config_routes.py -> Config dedup/NER + stats (delete movido a documents S26)
    +-- routes/tasks.py         -> Task polling
            |
            v
        dependencies.py
            |
            +-- get_container() -> ServiceContainer (14 properties)
            +-- get_current_user() -> JWT payload
                    |
                    v
            Application Services (6 servicios + 3 use cases)
                    |
                    v
            SQLite Repository (WAL mode)

La API no accede directamente a la base de datos. Toda la logica pasa por los Application Services del ServiceContainer.


Notas Tecnicas

  • API y Streamlit pueden ejecutarse simultaneamente (puertos 8000 y 8501)
  • Ambos comparten la misma base de datos SQLite en modo WAL
  • El ServiceContainer se inicializa una vez al arrancar uvicorn
  • Los modelos ML se pueden pre-cargar con SHERLOCK_PREWARM_MODELS=1
  • Logging via get_logger(__name__) en todos los modulos API
  • OCR se ejecuta en ThreadPoolExecutor (max 2 workers) por ser CPU-intensive

Version: 2.1 — 22 endpoints completos (S24 Foundation + S25 Completa + S25.1 Security + S26 Audit) Fecha: 2026-03-25 Endpoints activos: 22/22 funcionales (HALL-01 corregido en S26) Cambios S26: RBAC (H-01), rate limiting (H-02), security headers (M-02), CORS restrictivo (M-03), Swagger condicional (M-06), threshold eliminado de verify (D-08), delete movido a documents router (M-09), datetime ISO 8601 (L-03)