Tenants
Tenants
Prefijo: /api/tenants
Requiere Auth: Sí (excepto register-tenant)
Permiso requerido: tenants, *
Sistema completo de gestión multi-tenant. NeoPOS es una arquitectura multi-tenant pura: cada tenant representa una organización/empresa independiente con su propio aislamiento de datos. Todos los endpoints de negocio filtran automáticamente por tenant_id del JWT.
Roles involucrados:
| Rol | Acceso a Tenants |
|---|---|
| Superadmin (rol=3) | Gestión completa: crear, listar, editar, eliminar tenants, ver métricas, actividad y dashboard global |
| Admin (rol=1) | Ve SU propio tenant vía GET /api/tenants/current, GET /api/tenants/:tenant_id, GET /api/tenants/:tenant_id/metrics y GET /api/admin/tenants/:tenant_id/activity (solo si tenant_id del path == tenant_id del JWT). No tiene acceso cross-tenant |
| Asistente (rol=2) | No tiene acceso a gestión de tenants |
Ciclo de Vida de un Tenant
1. Registro (self-service) → POST /api/auth/register-tenant ↓2. Creación (Superadmin) → POST /api/tenants ↓3. Configuración → PUT /api/tenants/:id ↓4. Monitoreo → GET /api/tenants/:id/metrics ↓5. Dashboard Global → GET /api/admin/dashboard/overview ↓6. Eliminación → DELETE /api/tenants/:idPlanes y Facturación
Planes Disponibles
| Plan | plan | limite_usuarios | Descripción |
|---|---|---|---|
| Básico | BASICO | 3 | Plan inicial para negocios pequeños |
| Profesional | PRO | 10 | Funcionalidades completas para restaurantes |
| Premium | PREMIUM | Ilimitado | Todos los módulos y soporte prioritario |
| Enterprise | ENTERPRISE | Ilimitado | On-premise / dedicado, SLA personalizado |
Estados de un Tenant
| Estado | estado | Significado |
|---|---|---|
| Activo | ACTIVO | Funcionando normalmente |
| Inactivo | INACTIVO | Suspendido temporalmente (no puede operar) |
| Suspendido | SUSPENDIDO | Suspendido por impago o incumplimiento |
| Cancelado | CANCELADO | Cuenta cancelada definitivamente |
Control de expiración
El campo fecha_expiracion controla cuándo expira la suscripción. El dashboard global (GET /api/admin/dashboard/overview) lista automáticamente los tenants próximos a expirar (dentro de 30 días) para que el Superadmin pueda renovarlos o contactar al cliente.
Endpoints
Creación de Tenant (Self-Service)
POST /api/auth/register-tenant
Descripción: Crea un nuevo tenant junto con su usuario administrador inicial en una sola transacción atómica. Es el punto de entrada para nuevos clientes (registro autogestionado).
Requiere Auth: No — es un endpoint público para onboarding.
Contexto de uso: Este es el primer endpoint que se llama cuando un nuevo negocio se registra en NeoPOS. Crea simultáneamente:
- El tenant con su plan, límites y configuración inicial
- El primer usuario con rol Administrador (1) y flag
password_must_change: true - Los permisos por defecto para el nuevo tenant (semilla de módulos base)
Request:
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
tenant.codigo | string | ✅ | Código único del tenant (ej: MIEMPRESA) |
tenant.nombre | string | ✅ | Nombre comercial |
tenant.razon_social | string | ✅ | Razón social (NIT) |
tenant.nit | string | ✅ | NIT sin DV (ej: 900123456) |
tenant.dv | string | ❌ | Dígito de verificación (default 7) |
tenant.telefono | string | ❌ | Teléfono de contacto |
tenant.email | string | ✅ | Email corporativo |
tenant.direccion | string | ❌ | Dirección física |
tenant.ciudad | string | ❌ | Ciudad |
tenant.plan | string | ❌ | Plan: BASICO (default), PRO, PREMIUM, ENTERPRISE |
tenant.limite_usuarios | int | ❌ | Máximo usuarios (default: 3 para BASICO) |
admin_nombre | string | ✅ | Nombre del administrador |
admin_correo | string | ✅ | Email del administrador (debe ser único en el sistema) |
admin_password | string | ✅ | Contraseña (mín. 8 chars, mayúscula, número, caracter especial) |
{ "tenant": { "codigo": "MIEMPRESA", "nombre": "Mi Empresa SAS", "razon_social": "Mi Empresa SAS", "nit": "900123456", "dv": "7", "telefono": "3001234567", "email": "info@miempresa.com", "direccion": "Calle 123 #45-67", "ciudad": "Bogotá", "plan": "BASICO", "limite_usuarios": 3 }, "admin_nombre": "Admin Principal", "admin_correo": "admin@miempresa.com", "admin_password": "AdminPass1!"}Respuesta exitosa: 201 Created
Response:
{ "tenant": { "id": "550e8400-e29b-41d4-a716-446655440000", "codigo": "MIEMPRESA", "nombre": "Mi Empresa SAS", "estado": "ACTIVO", "plan": "BASICO" }, "admin": { "id": 2, "nombre": "Admin Principal", "correo": "admin@miempresa.com", "rol": 1, "password_must_change": true }, "mensaje": "Tenant 'Mi Empresa SAS' creado exitosamente"}Errores: 409 Conflict — el codigo del tenant o el admin_correo ya existen
cURL:
curl -X POST http://localhost/api/auth/register-tenant \ -H "Content-Type: application/json" \ -d '{ "tenant": { "codigo": "MIEMPRESA", "nombre": "Mi Empresa SAS", "razon_social": "Mi Empresa SAS", "nit": "900123456", "email": "info@miempresa.com", "plan": "BASICO" }, "admin_nombre": "Admin Principal", "admin_correo": "admin@miempresa.com", "admin_password": "AdminPass1!" }'Listar Tenants
GET /api/tenants
Descripción: Lista todos los tenants del sistema con paginación. Solo Superadmin.
Requiere Auth: Sí (rol=3)
Permiso requerido: tenants, leer
Parámetros query:
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
skip | int | ❌ | Registros a saltar (default 0) |
limit | int | ❌ | Máximo a retornar (default 50) |
Respuesta exitosa: 200 OK
Errores: 403 (no es Superadmin)
Response:
[ { "id": "550e8400-e29b-41d4-a716-446655440000", "codigo": "TENANT001", "nombre": "Mi Empresa SAS", "razon_social": "Mi Empresa SAS", "nit": "900123456-7", "dv": "7", "telefono": "3001234567", "email": "info@miempresa.com", "direccion": "Calle 123 #45-67", "ciudad": "Bogotá", "estado": "ACTIVO", "plan": "BASICO", "limite_usuarios": 3, "fecha_expiracion": "2027-01-01", "fecha_creacion": "2025-01-01T00:00:00", "fecha_actualizacion": "2025-01-15T00:00:00" }]cURL:
curl http://localhost/api/tenants -H "Authorization: Bearer <token>"Python:
import httpxresp = httpx.get("http://localhost/api/tenants", headers={"Authorization": f"Bearer :token"})tenants = resp.json()JavaScript:
const resp = await fetch('http://localhost/api/tenants', { headers: {'Authorization': `Bearer $:token`}});const tenants = await resp.json();Obtener Tenant Actual
GET /api/tenants/current
Descripción: Obtiene la información del tenant al que pertenece el usuario autenticado. Este es el único endpoint de tenants accesible para roles que no son Superadmin (Admin y Asistente pueden consultar su propio tenant).
Requiere Auth: Sí
Permiso requerido: No aplica (cualquier usuario autenticado)
Contexto de uso: Útil para que el frontend muestre información del negocio actual (nombre, plan, estado) en el panel de administración sin necesidad de permisos Superadmin.
Respuesta exitosa: 200 OK
Response: (misma estructura que TenantResponse)
Errores: 404 — tenant no encontrado (solo si el JWT tiene un tenant_id inválido)
cURL:
curl http://localhost/api/tenants/current -H "Authorization: Bearer <token>"Obtener Tenant por ID
GET /api/tenants/:tenant_id
Descripción: Obtiene un tenant por su UUID. Superadmin (cualquier tenant) o Administrador (solo SU tenant, tenant_id del JWT == tenant_id del path).
Requiere Auth: Sí
Permiso requerido: No consulta permisos_rol. Superadmin (rol=3) → cualquier tenant; Administrador (rol=1) → solo su propio tenant; Asistente (rol=2) u otro tenant → 403.
Parámetros de ruta:
| Parámetro | Tipo | Descripción |
|---|---|---|
tenant_id | string (UUID) | ID del tenant |
Respuesta exitosa: 200 OK — objeto TenantResponse completo
Errores: 403 (rol no autorizado o tenant ajeno), 404 (no encontrado)
cURL:
curl http://localhost/api/tenants/550e8400-e29b-41d4-a716-446655440000 \ -H "Authorization: Bearer <token>"Crear Tenant (Superadmin)
POST /api/tenants
Descripción: Crea un nuevo tenant directamente. A diferencia de register-tenant, este endpoint crea solo el tenant (sin usuario administrador). Útil para migraciones, importación masiva o creación por parte del Superadmin desde el panel de control.
Requiere Auth: Sí (rol=3)
Permiso requerido: tenants, crear
Request:
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
codigo | string | ✅ | Código único del tenant |
nombre | string | ✅ | Nombre comercial |
razon_social | string | ✅ | Razón social |
nit | string | ✅ | NIT |
dv | string | ❌ | Dígito de verificación |
telefono | string | ❌ | Teléfono |
email | string | ❌ | Email corporativo |
direccion | string | ❌ | Dirección |
ciudad | string | ❌ | Ciudad |
plan | string | ❌ | Plan (default BASICO) |
limite_usuarios | int | ❌ | Límite de usuarios (default 3) |
limite_terminals | int | ❌ | Límite de terminales POS (default 5). Controla cuántas terminales pueden registrarse/login en el tenant |
estado | string | ❌ | Estado (default ACTIVO) |
fecha_expiracion | date | ❌ | Fecha de expiración de la suscripción |
{ "codigo": "NUEVOTENANT", "nombre": "Nueva Empresa SAS", "razon_social": "Nueva Empresa SAS", "nit": "900987654-3", "telefono": "3007654321", "email": "info@nuevaempresa.com", "direccion": "Carrera 50 #20-30", "ciudad": "Medellín", "plan": "PRO", "limite_usuarios": 10, "estado": "ACTIVO", "fecha_expiracion": "2027-06-30"}Respuesta exitosa: 201 Created — objeto TenantResponse completo
Errores: 403 (no Superadmin), 409 (código duplicado)
cURL:
curl -X POST http://localhost/api/tenants \ -H "Content-Type: application/json" \ -H "Authorization: Bearer <token>" \ -d '{"codigo":"NUEVOTENANT","nombre":"Nueva Empresa SAS","nit":"900987654-3","plan":"PRO","limite_usuarios":10}'Actualizar Tenant
PUT /api/tenants/:tenant_id
Descripción: Actualiza la configuración de un tenant: plan, límites, estado, datos de contacto, fecha de expiración. Solo Superadmin.
Requiere Auth: Sí (rol=3)
Permiso requerido: tenants, actualizar
Contexto de uso: Este es el endpoint principal para el control de planes y facturación:
- Cambiar de plan (
BASICO→PRO→PREMIUM) - Aumentar límite de usuarios
- Extender/suspender la cuenta (
estadoofecha_expiracion) - Actualizar datos de contacto del negocio
Request: (todos los campos opcionales — solo enviar los que se modifican)
| Campo | Tipo | Descripción |
|---|---|---|
nombre | string | Nombre comercial |
razon_social | string | Razón social |
nit | string | NIT |
telefono | string | Teléfono |
email | string | Email corporativo |
direccion | string | Dirección |
ciudad | string | Ciudad |
estado | string | Estado: ACTIVO, INACTIVO, SUSPENDIDO, CANCELADO |
plan | string | Plan: BASICO, PRO, PREMIUM, ENTERPRISE |
limite_usuarios | int | Máximo de usuarios permitidos |
limite_terminals | int | Máximo de terminales POS del tenant |
fecha_expiracion | date | Nueva fecha de expiración |
{ "nombre": "Mi Empresa SAS Actualizado", "plan": "PREMIUM", "limite_usuarios": 10, "estado": "ACTIVO", "fecha_expiracion": "2027-12-31"}Respuesta exitosa: 200 OK — objeto TenantResponse completo actualizado
Errores: 403 (no Superadmin), 404 (no encontrado)
cURL:
curl -X PUT http://localhost/api/tenants/550e8400-e29b-41d4-a716-446655440000 \ -H "Content-Type: application/json" \ -H "Authorization: Bearer <token>" \ -d '{"plan":"PREMIUM","limite_usuarios":10,"fecha_expiracion":"2027-12-31"}'Eliminar Tenant
DELETE /api/tenants/:tenant_id
Descripción: Elimina un tenant y todos sus datos asociados (usuarios, ventas, productos, etc.). Esta operación es definitiva e irreversible. Solo Superadmin.
Requiere Auth: Sí (rol=3)
Permiso requerido: tenants, eliminar
Parámetros de ruta:
| Parámetro | Tipo | Descripción |
|---|---|---|
tenant_id | string (UUID) | ID del tenant a eliminar |
Advertencia: Esta operación elimina físicamente todos los registros del tenant (cascada por FK). No hay soft delete a nivel de tenant.
Respuesta exitosa: 200 OK
{ "mensaje": "Tenant eliminado exitosamente"}Errores: 403 (no Superadmin), 404 (no encontrado)
cURL:
curl -X DELETE http://localhost/api/tenants/550e8400-e29b-41d4-a716-446655440000 \ -H "Authorization: Bearer <token>"Monitoreo y Métricas
Métricas por Tenant
GET /api/tenants/:tenant_id/metrics
Descripción: Obtiene métricas específicas de un tenant: usuarios, ventas (hoy/mes), productos, créditos pendientes, terminales activas. Superadmin (cualquier tenant) o Administrador (solo SU tenant).
Requiere Auth: Sí
Permiso requerido: No consulta permisos_rol. Superadmin (rol=3) → cualquier tenant; Administrador (rol=1) → solo su propio tenant; Asistente (rol=2) u otro tenant → 403.
Contexto de uso: Dashboard de administración para que el Superadmin vea el estado operativo de cada tenant sin necesidad de acceder al tenant. El Admin (rol=1) lo usa para ver su propio detalle en el panel. Útil para:
- Monitorear actividad reciente de un cliente
- Verificar si un tenant está usando el sistema
- Detectar tenants inactivos o con baja operación
- Reportes de uso por plan
Respuesta exitosa: 200 OK
{ "tenant_id": "550e8400-e29b-41d4-a716-446655440000", "nombre": "Mi Empresa SAS", "estado": "ACTIVO", "plan": "PRO", "usuarios": { "total": 8 }, "ventas": { "hoy": 25, "total_hoy": 1250000, "mes": 650, "total_mes": 32500000 }, "productos": { "total": 150 }, "creditos": { "pendientes": 15 }, "terminales": { "total": 3 }}Campos del response:
| Campo | Tipo | Descripción |
|---|---|---|
tenant_id | string | UUID del tenant |
nombre | string | Nombre comercial |
estado | string | Estado actual (ACTIVO, INACTIVO, etc.) |
plan | string | Plan contratado |
usuarios.total | int | Cantidad total de usuarios registrados |
ventas.hoy | int | Número de ventas del día de hoy |
ventas.total_hoy | int | Suma monetaria de ventas hoy |
ventas.mes | int | Número de ventas del mes actual |
ventas.total_mes | int | Suma monetaria de ventas del mes |
productos.total | int | Total de productos en catálogo |
creditos.pendientes | int | Créditos no pagados |
terminales.total | int | Terminales POS registradas |
Errores: 403 (rol no autorizado o tenant ajeno), 404 (tenant no encontrado)
cURL:
curl http://localhost/api/tenants/550e8400-e29b-41d4-a716-446655440000/metrics \ -H "Authorization: Bearer <token>"JavaScript:
const resp = await fetch('http://localhost/api/tenants/550e8400-e29b-41d4-a716-446655440000/metrics', { headers: {'Authorization': `Bearer $:token`}});const metrics = await resp.json();// metrics.ventas.hoy → 25// metrics.usuarios.total → 8// metrics.plan → "PRO"Dashboard Global (Cross-Tenant)
GET /api/admin/dashboard/overview
Descripción: Visión general de todo el sistema para el Superadmin. Incluye conteo de tenants activos/inactivos, tenants próximos a expirar, total de usuarios, ventas globales del día/mes y estado del sistema (sync, cloud).
Requiere Auth: Sí (rol=3)
Permiso requerido: tenants, leer
Contexto de uso: Pantalla principal del panel Superadmin. Muestra un resumen ejecutivo del estado de toda la plataforma:
- Tenants: Cuántos activos, inactivos y próximos a vencer
- Usuarios: Total de usuarios en el sistema
- Ventas globales: Hoy y en el mes (todos los tenants combinados)
- Estado del sistema: Conectividad cloud, salud de la cola de sync, uptime
Respuesta exitosa: 200 OK
{ "tenants_activos": 5, "tenants_inactivos": 1, "tenants_proximos_a_expirar": [ { "id": "550e8400-e29b-41d4-a716-446655440000", "nombre": "Mi Empresa SAS", "fecha_expiracion": "2026-08-15", "dias_restantes": 16 } ], "total_usuarios": 42, "ventas_hoy": 87, "ventas_totales_hoy": 4350000, "ventas_mes": 2100, "ventas_totales_mes": 105000000, "sync_pendiente_global": 3, "estado_sistema": { "cloud_connected": true, "sync_queue_ok": true, "uptime_horas": 720 }}Campos del response:
| Campo | Tipo | Descripción |
|---|---|---|
tenants_activos | int | Tenants con estado ACTIVO |
tenants_inactivos | int | Tenants con estado distinto a ACTIVO |
tenants_proximos_a_expirar | array | Tenants activos cuya suscripción expira en ≤30 días |
tenants_proximos_a_expirar[].dias_restantes | int | Días hasta la expiración |
total_usuarios | int | Usuarios totales en todos los tenants |
ventas_hoy | int | Ventas totales de hoy (cross-tenant) |
ventas_totales_hoy | int | Valor monetario total de ventas hoy |
ventas_mes | int | Ventas totales del mes |
ventas_totales_mes | int | Valor monetario total del mes |
sync_pendiente_global | int | Eventos pendientes de sincronización |
estado_sistema.cloud_connected | bool | Conectividad con PostgreSQL |
estado_sistema.sync_queue_ok | bool | Cola de sync sin acumulación (< 100) |
estado_sistema.uptime_horas | int | Horas desde el último reinicio del API |
Errores: 403 (no Superadmin)
cURL:
curl http://localhost/api/admin/dashboard/overview \ -H "Authorization: Bearer <token>"JavaScript:
const resp = await fetch('http://localhost/api/admin/dashboard/overview', { headers: {'Authorization': `Bearer $:token`}});const overview = await resp.json();// overview.tenants_activos → 5// overview.tenants_proximos_a_expirar → [{...}]Métricas del Sistema
GET /api/system/metrics
Descripción: Métricas del servidor donde corre la API: CPU, RAM, disco, uptime, versión de Python, estado del cloud y del orquestador de sync.
Requiere Auth: Sí (rol=3)
Permiso requerido: tenants, leer
Contexto de uso: Monitoreo de infraestructura. Permite al Superadmin verificar la salud del servidor sin acceso SSH.
Respuesta exitosa: 200 OK
{ "cpu_percent": 23.5, "memory": { "total_bytes": 8589934592, "used_bytes": 4294967296, "available_bytes": 4294967296, "percent": 50.0 }, "disk": { "total_bytes": 107374182400, "used_bytes": 75161927680, "free_bytes": 32212254720, "percent": 70.0 }, "uptime_seconds": 2592000, "python_version": "3.12.4", "platform": "linux", "cloud_enabled": true, "sync_running": true}Errores: 403 (no Superadmin)
cURL:
curl http://localhost/api/system/metrics -H "Authorization: Bearer <token>"GET /api/system/db/metrics
Descripción: Métricas de la base de datos: tipo de BD en uso (PostgreSQL o SQLite), conteo de registros por tabla principal (ventas, productos, clientes, usuarios, tenants, créditos).
Requiere Auth: Sí (rol=3)
Permiso requerido: tenants, leer
Contexto de uso: Permite verificar el volumen de datos en cada tabla del sistema. Útil para diagnóstico de rendimiento y planificación de capacidad.
Respuesta exitosa: 200 OK
{ "database": "postgresql", "tables": { "ventas": 15420, "productos": 850, "clientes": 3200, "usuarios": 42, "tenants": 6, "creditos": 450 }}Errores: 403 (no Superadmin)
cURL:
curl http://localhost/api/system/db/metrics -H "Authorization: Bearer <token>"Resumen de Endpoints
| Método | Ruta | Rol | Permiso | Propósito |
|---|---|---|---|---|
POST | /api/auth/register-tenant | Público | — | Registro autogestionado de nuevo tenant + admin |
GET | /api/tenants | Superadmin | tenants, leer | Listar todos los tenants |
GET | /api/tenants/current | Cualquiera | — | Info del tenant actual |
GET | /api/tenants/:id | Superadmin/Admin (solo su tenant) | híbrido | Detalle de un tenant |
POST | /api/tenants | Superadmin | tenants, crear | Crear tenant (sin usuario) |
PUT | /api/tenants/:id | Superadmin | tenants, actualizar | Actualizar plan/estado/límites |
DELETE | /api/tenants/:id | Superadmin | tenants, eliminar | Eliminar tenant definitivamente |
GET | /api/tenants/:id/metrics | Superadmin/Admin (solo su tenant) | híbrido | Métricas operativas del tenant |
GET | /api/admin/tenants/:id/activity | Superadmin/Admin (solo su tenant) | híbrido | Actividad por usuario del tenant |
GET | /api/admin/dashboard/overview | Superadmin | tenants, leer | Dashboard global cross-tenant |
GET | /api/system/metrics | Superadmin | tenants, leer | Métricas del servidor (CPU/RAM/disco) |
GET | /api/system/db/metrics | Superadmin | tenants, leer | Métricas de BD (conteo por tabla) |