Skip to content

API Maslow (externa)

Maslow es la aplicación Flask de RR.HH. (empleados, organización, sincronización). Este documento cubre los endpoints pensados para ser llamados por sistemas externos y scripts (no los del navegador, que requieren sesión).

Repo fuente: maslow, branch main.

Cómo conectarse

  • No hay host productivo committed. Maslow se corre en desarrollo en http://127.0.0.1:5051 (flask run --debug --port 5051). El host productivo (si existe) se provee externamente; usá <host> como la base de tu instancia.
  • Sin prefijo /api global. Los prefijos son por blueprint (/api/sync, /api/employees, /api/org, /api/users).

Autenticación

Mecanismo Header Variables de entorno Dónde
API Key X-API-Key: <key> API_KEY /api/sync/* (menos /ids)
HTTP Basic Authorization: Basic <base64(user:pass)> BASIC_AUTH_USERNAME / BASIC_AUTH_PASSWORD /api/sync/ids
Público /api/employees, /api/org, /api/users, /health

⚠️ Postura de seguridad a tener en cuenta: - Si API_KEY no está seteada, los endpoints /api/sync/* quedan abiertos (modo dev). En producción debe configurarse. - Si BASIC_AUTH_USERNAME y BASIC_AUTH_PASSWORD ambos no están seteados, /api/sync/ids queda abierto. - Los endpoints /api/employees, /api/org y /api/users no tienen autenticación (CRUD público). Probablemente pensados para el frontend, pero son llamables externamente — conviene protegerlos con un reverse proxy. - api_key_required compara con != (no es constante-tiempo).

En el resto del documento, <host> = la base de la instancia.


Healthcheck

GET  <host>/health        # público → {"status":"healthy"}

Sincronización — /api/sync

Superficie machine-to-machine para que sistemas externos lean datos de Maslow.

GET /api/sync/trabajadores [X-API-Key]

Empleados formateados para la tabla externa Trabajador. Ordenados por updated_at DESC.

?include_inactive=false   # bool, default false
&include_manager=false    # bool, default false (incluye objeto jefe_directo)
&updated_since=2026-01-01T00:00:00   # ISO datetime, filtro incremental

400 {"error":"Invalid updated_since format. Use ISO format."} si la fecha no es parseable. Por defecto excluye inactivos; jefe_directo sólo viene si include_manager=true.

GET /api/sync/trabajadores/<rut> [X-API-Key]

Un empleado por RUT (acepta con/sin puntos y guion).

?include_manager=false

404 {"error":"Employee not found"}; 400 {"error":"Invalid RUT format"}.

GET /api/sync/trabajadores/krp [X-API-Key]

Empleados activos formateados para la integración KRP / logisticasbip: { rut, dv, first_name, paterno, materno, email } (email con fallback a corporate_email).

GET /api/sync/centros-costo [X-API-Key]

Lista todos los centros de costo para mapeo: { count, centros_costo:[{ code, name, _maslow_id }] }.

GET /api/sync/ids [HTTP Basic]

RUT + IDs externos para cross-referenciar sincronizaciones.

?include_inactive=false   # bool, default false

{ count, employees:[{ rut, id_toa, id_krp }] }. (Nota: usa Basic, no API Key.)

GET /api/sync/stats [X-API-Key]

Métricas de calidad de datos para planificar sincronización: { total_active, total_inactive, with_email, without_email, with_cost_center, without_cost_center, duplicate_emails:[...] }.


Empleados — /api/employees [público]

CRUD completo de empleados (sin auth — ver advertencia arriba).

Listado y búsqueda

GET /api/employees?q=<texto>&cost_center_id=&position_id=&group_id=
                    &employment_type_id=&manager_id=&is_active=true
                    &include_relations=false

q busca ILIKE en first_name/paterno/materno/rut. Devuelve { employees:[...], count }.

Obtener uno

GET /api/employees/<int:id>           # ?include_relations=true (default acá)
GET /api/employees/rut/<rut>          # acepta 12.345.678-9; relations siempre incluidas

404 {"error":"Employee not found"}.

Crear

POST /api/employees
{ "rut": "12345678", "dv": "9", "first_name": "...", "paterno": "...",
  "materno": "?", "gender": "?", "nationality": "?", "birth_date": "?",
  "marital_status": "?", "hire_date": "?", "drivers_licence_expiry": "?",
  "nubox_code": "?", "id_krp": "?", "id_toa": "?", "cost_center_id": "?",
  "position_id": "?", "group_id": "?", "employment_type_id": "?",
  "manager_id": "?" }

Requeridos: rut, dv, first_name, paterno. Fechas YYYY-MM-DD. 400 {"errors":[...]} (faltantes); 409 {"error":"Employee with RUT <rut> already exists"}; 201 {"employee":{...}}.

Cambia PositionHistory / CostCenterHistory / ManagerAssignment cuando esos FK cambian.

Actualizar / desactivar / reactivar

PUT    /api/employees/<int:id>                  # cualquier campo; fechas YYYY-MM-DD
DELETE /api/employees/<int:id>                  # soft delete
POST   /api/employees/<int:id>/reactivate

DELETE requiere body {"termination_date":"YYYY-MM-DD"} → cierra los historiales abiertos; responde {"message":"Employee deactivated","employee":{...}}. reactivate400 {"error":"Employee is already active"} si ya está activo.

Sub-recursos relacionados (todos PUT)

PUT /api/employees/<id>/contact     # address, neighborhood, comuna, email, phone,
                                    #   corporate_email, corporate_phone, contract_address_city
PUT /api/employees/<id>/banking     # bank_name, account_type, account_number
PUT /api/employees/<id>/benefits    # pension_provider, health_provider, health_plan_amount, seremi_registration
PUT /api/employees/<id>/uniform     # shirt_size, jacket_size, pants_size, shoe_size

Create-or-update (sólo setea claves que existen en el modelo). 404 si el empleado no existe.


Organización — /api/org [público]

CRUD sobre cuatro entidades de referencia (todas sin auth). Patrón uniforme: GET lista, GET /<id> uno, POST crea, PUT /<id> actualiza. Las listas aceptan ?include_inactive=false.

Centros de costo — /api/org/cost-centers

POST  /api/org/cost-centers        { "code": "<req>", "name": "?" }
PUT   /api/org/cost-centers/<id>   { campos }

400 {"error":"Code is required"}; 409 {"error":"Cost center with code '<code>' already exists"}.

Cargos — /api/org/positions

POST  { "title": "<req>", "description": "?" }

400 {"error":"Title is required"}; 409 si el título ya existe.

Grupos — /api/org/groups

POST  { "code": "<req>", "name": "?" }

400 {"error":"Code is required"}; 409 si el código ya existe.

Tipos de contrato — /api/org/employment-types

POST  { "code": "<req>", "name": "?", "description": "?" }

400 {"error":"Code is required"}; 409 si el código ya existe.


Usuarios — /api/users [público]

GET  /api/users/                # lista todos → { "users":[ <user.to_dict()> ] }  (ojo con el slash final)
GET  /api/users/<int:id>        # uno → { "user": <user.to_dict()> } ; 404 {"error":"User not found"}

Sin auth (ver advertencia).


Notas

  • Esta doc cubre sólo la superficie externa/programática. Los endpoints de export/import Excel (/api/export/*, /api/import/*) requieren sesión de navegador (login en /auth/login → cookie) y no se documentan aquí.
  • Login (sesión, no token): POST /auth/login con formulario email+password → setea cookie de sesión. No hay /auth/token, /auth/me ni JWT. Roles: admin, rrhh, viewer.
  • El único endpoint AJAX de la app fuera de /api/* es PATCH /empleados/<id>/id-toa (sesión + rol rrhh); también fuera de alcance.