Files
OmniOilPersonal/services/backend-api/static/openapi.yaml
Wilman Yesid Farfan Diaz 58db4c76a7 feat(api): Phase 6 — OpenAPI docs, version header, enhanced health endpoint
- Add static OpenAPI 3.0.3 spec (services/backend-api/static/openapi.yaml)
  covering all ~70 endpoints with schemas, security, tags and responses
- Serve Swagger UI at GET /docs (embedded HTML, dark OmniOil theme)
- Serve raw YAML at GET /docs/openapi.yaml (include_str! at compile time)
- Add Tower middleware (api_version.rs) injecting X-API-Version: 1 and
  X-Powered-By: OmniOil/1.0 on every response
- Replace static /health handler with enhanced health_check that probes
  DB latency, tracks uptime via OnceLock, returns structured HealthResponse
- Expand API Explorer frontend: 70+ endpoints in 10 groups, PATCH color
  coding (purple), group filter pills, count badge, Swagger UI links

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-03 15:14:50 -05:00

2390 lines
63 KiB
YAML

openapi: 3.0.3
info:
title: OmniOil SCADA API
description: |
API de la plataforma OmniOil para telemetría IoT/SCADA en el sector de hidrocarburos.
Cumple con la Resolución 0651 de la ANH (Agencia Nacional de Hidrocarburos, Colombia).
## Autenticación
La mayoría de endpoints requieren un Bearer JWT token en el header `Authorization`.
Obtén tu token con `POST /api/auth/login`.
## Versionado
La API actual es la versión 1. Todos los endpoints incluyen el header `X-API-Version: 1` en las respuestas.
## Cumplimiento ANH
Los endpoints de telemetría e informes cumplen con la normativa colombiana de
reporte de producción de hidrocarburos (Resolución ANH 0651).
version: "1.0.0"
contact:
name: OmniOil Support
email: soporte@omnioil.app
license:
name: Propietario
servers:
- url: /api
description: API Server (via proxy)
- url: http://localhost:8000/api
description: Desarrollo local
components:
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
schemas:
LoginRequest:
type: object
required: [email, password]
properties:
email:
type: string
format: email
example: admin@omnioil.app
password:
type: string
format: password
example: "••••••••"
LoginResponse:
type: object
properties:
token:
type: string
description: JWT access token
role:
type: string
enum: [superadmin, admin, supervisor, operador, anh_reader]
projects:
type: array
items:
type: object
properties:
id:
type: string
format: uuid
name:
type: string
User:
type: object
properties:
id:
type: string
format: uuid
email:
type: string
format: email
name:
type: string
role:
type: string
enum: [superadmin, admin, supervisor, operador, anh_reader]
is_active:
type: boolean
projects:
type: array
items:
type: string
format: uuid
created_at:
type: string
format: date-time
CreateUserRequest:
type: object
required: [email, name, role, password]
properties:
email:
type: string
format: email
name:
type: string
role:
type: string
enum: [superadmin, admin, supervisor, operador, anh_reader]
password:
type: string
minLength: 8
EdgeProject:
type: object
properties:
id:
type: string
format: uuid
name:
type: string
description:
type: string
location:
type: string
is_active:
type: boolean
last_health_report:
type: object
nullable: true
created_at:
type: string
format: date-time
CreateProjectRequest:
type: object
required: [name]
properties:
name:
type: string
example: "Campo Norte - Pozo 01"
description:
type: string
location:
type: string
EdgeAsset:
type: object
properties:
id:
type: string
format: uuid
project_id:
type: string
format: uuid
name:
type: string
asset_type:
type: string
enum: [well, tank, pipeline, station, meter]
latitude:
type: number
format: double
nullable: true
longitude:
type: number
format: double
nullable: true
created_at:
type: string
format: date-time
CreateAssetRequest:
type: object
required: [project_id, name, asset_type]
properties:
project_id:
type: string
format: uuid
name:
type: string
example: "Pozo P-01"
asset_type:
type: string
enum: [well, tank, pipeline, station, meter]
latitude:
type: number
format: double
longitude:
type: number
format: double
EdgeDevice:
type: object
properties:
id:
type: string
format: uuid
asset_id:
type: string
format: uuid
name:
type: string
protocol:
type: string
enum: [modbus_tcp, modbus_rtu, opc_ua, s7, dnp3]
host:
type: string
nullable: true
port:
type: integer
nullable: true
slave_id:
type: integer
nullable: true
enabled:
type: boolean
CreateDeviceRequest:
type: object
required: [name, protocol]
properties:
name:
type: string
protocol:
type: string
enum: [modbus_tcp, modbus_rtu, opc_ua, s7, dnp3]
host:
type: string
port:
type: integer
slave_id:
type: integer
enabled:
type: boolean
default: true
EdgeVariable:
type: object
properties:
id:
type: string
format: uuid
device_id:
type: string
format: uuid
alias:
type: string
data_type:
type: string
enum: [float, int, bool, string]
register_address:
type: integer
nullable: true
unit:
type: string
nullable: true
scale_factor:
type: number
nullable: true
enabled:
type: boolean
CreateVariableRequest:
type: object
required: [alias, data_type]
properties:
alias:
type: string
example: "temperatura_cabezal"
data_type:
type: string
enum: [float, int, bool, string]
register_address:
type: integer
unit:
type: string
example: "°C"
scale_factor:
type: number
enabled:
type: boolean
default: true
TelemetryPayload:
type: object
required: [asset_id, readings]
properties:
asset_id:
type: string
format: uuid
timestamp:
type: string
format: date-time
readings:
type: array
items:
type: object
required: [variable, value]
properties:
variable:
type: string
example: "presion_cabezal"
value:
type: number
unit:
type: string
AlarmEvent:
type: object
properties:
id:
type: string
format: uuid
project_id:
type: string
format: uuid
asset_id:
type: string
format: uuid
nullable: true
variable:
type: string
value:
type: number
alarm_type:
type: string
enum: [high, low, critical_high, critical_low, comm_loss]
message:
type: string
acknowledged:
type: boolean
timestamp:
type: string
format: date-time
AlertRule:
type: object
properties:
id:
type: string
format: uuid
project_id:
type: string
format: uuid
name:
type: string
variable_alias:
type: string
condition:
type: string
enum: [gt, lt, gte, lte, eq]
threshold:
type: number
severity:
type: string
enum: [info, warning, critical]
is_enabled:
type: boolean
created_at:
type: string
format: date-time
CreateAlertRuleRequest:
type: object
required: [project_id, name, variable_alias, condition, threshold, severity]
properties:
project_id:
type: string
format: uuid
name:
type: string
variable_alias:
type: string
example: "presion_cabezal"
condition:
type: string
enum: [gt, lt, gte, lte, eq]
threshold:
type: number
example: 150.0
severity:
type: string
enum: [info, warning, critical]
BillingUsage:
type: object
properties:
project_id:
type: string
format: uuid
period_start:
type: string
format: date-time
period_end:
type: string
format: date-time
telemetry_points:
type: integer
active_devices:
type: integer
plan:
type: string
enum: [starter, professional, enterprise]
estimated_cost_usd:
type: number
format: double
Invoice:
type: object
properties:
id:
type: string
format: uuid
project_id:
type: string
format: uuid
period:
type: string
example: "2024-01"
amount_usd:
type: number
format: double
status:
type: string
enum: [pending, paid, overdue, cancelled]
issued_at:
type: string
format: date-time
paid_at:
type: string
format: date-time
nullable: true
ANHReport:
type: object
properties:
id:
type: string
format: uuid
project_id:
type: string
format: uuid
report_date:
type: string
format: date
period_start:
type: string
format: date-time
period_end:
type: string
format: date-time
status:
type: string
enum: [pending, generated, submitted]
hash_sha384:
type: string
nullable: true
created_at:
type: string
format: date-time
HealthResponse:
type: object
properties:
status:
type: string
enum: [ok, degraded, error]
version:
type: string
example: "1.0.0"
timestamp:
type: string
format: date-time
uptime_seconds:
type: integer
services:
type: object
properties:
database:
$ref: '#/components/schemas/ServiceStatus'
api:
$ref: '#/components/schemas/ServiceStatus'
ServiceStatus:
type: object
properties:
status:
type: string
enum: [ok, error]
latency_ms:
type: integer
nullable: true
message:
type: string
nullable: true
ErrorResponse:
type: object
properties:
error:
type: string
example: "No autorizado"
security:
- BearerAuth: []
tags:
- name: Auth
description: Autenticación y gestión de sesiones
- name: Users
description: Gestión de usuarios (admin)
- name: Dashboard
description: Datos del dashboard principal
- name: Telemetry
description: Ingesta y consulta de telemetría
- name: ANH Reports
description: Reportes de cumplimiento ANH Resolución 0651
- name: Alerts
description: Reglas de alerta y alarmas activas
- name: Operations
description: Operaciones, pruebas y registros ANH 0651
- name: Billing
description: Facturación y suscripciones
- name: Edge Projects
description: Proyectos de telemetría edge
- name: Edge Assets
description: Activos (pozos, tanques, estaciones)
- name: Edge Devices
description: Dispositivos de campo (PLCs, RTUs, Modbus)
- name: Edge Variables
description: Variables y tags de los dispositivos
- name: System
description: Estado del sistema y métricas
paths:
# ── Auth ──────────────────────────────────────────────────────────────────
/auth/login:
post:
tags: [Auth]
summary: Iniciar sesión
security: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/LoginRequest'
responses:
'200':
description: Login exitoso
content:
application/json:
schema:
$ref: '#/components/schemas/LoginResponse'
'401':
description: Credenciales inválidas
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'429':
description: Demasiados intentos (rate limited)
/auth/register:
post:
tags: [Auth]
summary: Registrar nuevo usuario (deshabilitado en producción)
security: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUserRequest'
responses:
'201':
description: Usuario creado
'403':
description: Registro deshabilitado en producción
'429':
description: Rate limited
/auth/refresh:
post:
tags: [Auth]
summary: Renovar access token usando refresh token
security: []
responses:
'200':
description: Token renovado
content:
application/json:
schema:
$ref: '#/components/schemas/LoginResponse'
'401':
description: Refresh token inválido o expirado
/auth/logout:
post:
tags: [Auth]
summary: Cerrar sesión e invalidar tokens
responses:
'200':
description: Sesión cerrada exitosamente
/auth/setup/status:
get:
tags: [Auth]
summary: Verificar si el sistema requiere configuración inicial
security: []
responses:
'200':
description: Estado de setup
content:
application/json:
schema:
type: object
properties:
requires_setup:
type: boolean
has_users:
type: boolean
/auth/setup/register:
post:
tags: [Auth]
summary: Crear primer administrador (solo cuando no hay usuarios)
security: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUserRequest'
responses:
'201':
description: Administrador inicial creado
content:
application/json:
schema:
$ref: '#/components/schemas/LoginResponse'
'409':
description: El sistema ya fue configurado
# ── Users (Admin) ─────────────────────────────────────────────────────────
/admin/users:
get:
tags: [Users]
summary: Listar todos los usuarios del sistema
responses:
'200':
description: Lista de usuarios
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/User'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
post:
tags: [Users]
summary: Crear nuevo usuario
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUserRequest'
responses:
'201':
description: Usuario creado
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
/admin/users/{id}:
get:
tags: [Users]
summary: Obtener usuario por ID
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Datos del usuario
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
put:
tags: [Users]
summary: Actualizar datos de un usuario
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUserRequest'
responses:
'200':
description: Usuario actualizado
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
/admin/users/{id}/reset-password:
post:
tags: [Users]
summary: Resetear contraseña de un usuario
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [new_password]
properties:
new_password:
type: string
minLength: 8
responses:
'200':
description: Contraseña actualizada
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
/admin/users/{id}/projects:
put:
tags: [Users]
summary: Asignar proyectos edge al usuario
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [project_ids]
properties:
project_ids:
type: array
items:
type: string
format: uuid
responses:
'200':
description: Proyectos asignados
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
/admin/roles:
get:
tags: [Users]
summary: Listar roles disponibles en el sistema
responses:
'200':
description: Lista de roles
content:
application/json:
schema:
type: array
items:
type: string
enum: [superadmin, admin, supervisor, operador, anh_reader]
'401':
$ref: '#/components/responses/Unauthorized'
# ── Dashboard ─────────────────────────────────────────────────────────────
/dashboard/summary:
get:
tags: [Dashboard]
summary: Resumen del dashboard con estadísticas del proyecto activo
responses:
'200':
description: Datos del dashboard
content:
application/json:
schema:
type: object
properties:
active_assets:
type: integer
total_alarms:
type: integer
recent_telemetry:
type: array
items:
type: object
compliance_status:
type: string
'401':
$ref: '#/components/responses/Unauthorized'
# ── Telemetry ─────────────────────────────────────────────────────────────
/telemetry:
post:
tags: [Telemetry]
summary: Enviar datos de telemetría desde campo hacia la plataforma
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TelemetryPayload'
responses:
'201':
description: Telemetría ingresada correctamente
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
/telemetry/{asset_id}:
get:
tags: [Telemetry]
summary: Obtener telemetría reciente de un asset
parameters:
- name: asset_id
in: path
required: true
schema:
type: string
format: uuid
- name: limit
in: query
schema:
type: integer
default: 100
responses:
'200':
description: Lecturas de telemetría recientes
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/TelemetryPayload'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
/telemetry/{asset_id}/history:
get:
tags: [Telemetry]
summary: Historial agregado de telemetría en ventanas de 5 minutos (ANH)
parameters:
- name: asset_id
in: path
required: true
schema:
type: string
format: uuid
- name: from
in: query
schema:
type: string
format: date-time
- name: to
in: query
schema:
type: string
format: date-time
responses:
'200':
description: Historial agregado de telemetría
content:
application/json:
schema:
type: array
items:
type: object
properties:
bucket:
type: string
format: date-time
variable:
type: string
avg_value:
type: number
min_value:
type: number
max_value:
type: number
'401':
$ref: '#/components/responses/Unauthorized'
# ── ANH Reports ───────────────────────────────────────────────────────────
/anh/reports:
get:
tags: [ANH Reports]
summary: Listar historial de reportes ANH generados (Resolución 0651)
parameters:
- name: project_id
in: query
schema:
type: string
format: uuid
responses:
'200':
description: Lista de reportes ANH
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/ANHReport'
'401':
$ref: '#/components/responses/Unauthorized'
/anh/reports/{id}:
get:
tags: [ANH Reports]
summary: Obtener detalle y contenido JSON de un reporte ANH
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Detalle del reporte ANH
content:
application/json:
schema:
$ref: '#/components/schemas/ANHReport'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
/anh/reports/{id}/download:
get:
tags: [ANH Reports]
summary: Descargar JSON oficial del reporte ANH para validación externa
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Archivo JSON del reporte
content:
application/json:
schema:
type: object
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
/anh/reports/{id}/hash:
get:
tags: [ANH Reports]
summary: Descargar hash SHA-384 del reporte para auditoría de integridad
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Hash SHA-384 del reporte
content:
text/plain:
schema:
type: string
example: "384:abc123..."
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
# ── Alerts & Alarms ───────────────────────────────────────────────────────
/alert-rules:
get:
tags: [Alerts]
summary: Listar reglas de alerta del proyecto
parameters:
- name: project_id
in: query
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Lista de reglas de alerta
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/AlertRule'
'401':
$ref: '#/components/responses/Unauthorized'
post:
tags: [Alerts]
summary: Crear nueva regla de alerta
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateAlertRuleRequest'
responses:
'201':
description: Regla creada
content:
application/json:
schema:
$ref: '#/components/schemas/AlertRule'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
/alert-rules/{id}:
put:
tags: [Alerts]
summary: Actualizar una regla de alerta existente
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateAlertRuleRequest'
responses:
'200':
description: Regla actualizada
content:
application/json:
schema:
$ref: '#/components/schemas/AlertRule'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
delete:
tags: [Alerts]
summary: Eliminar regla de alerta
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'204':
description: Regla eliminada
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
/alert-rules/{id}/toggle:
patch:
tags: [Alerts]
summary: Activar o desactivar una regla de alerta
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Estado de la regla actualizado
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
/alarms:
get:
tags: [Alerts]
summary: Listar alarmas activas del proyecto
parameters:
- name: project_id
in: query
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Lista de alarmas activas
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/AlarmEvent'
'401':
$ref: '#/components/responses/Unauthorized'
# ── Tanks ─────────────────────────────────────────────────────────────────
/tanks:
get:
tags: [Edge Assets]
summary: Listar tanques del proyecto activo
parameters:
- name: project_id
in: query
schema:
type: string
format: uuid
responses:
'200':
description: Lista de tanques
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/EdgeAsset'
'401':
$ref: '#/components/responses/Unauthorized'
# ── Operations ANH 0651 ───────────────────────────────────────────────────
/movements:
get:
tags: [Operations]
summary: Listar movimientos operativos registrados
parameters:
- name: project_id
in: query
schema:
type: string
format: uuid
- name: from
in: query
schema:
type: string
format: date-time
- name: to
in: query
schema:
type: string
format: date-time
responses:
'200':
description: Lista de movimientos
content:
application/json:
schema:
type: array
items:
type: object
'401':
$ref: '#/components/responses/Unauthorized'
post:
tags: [Operations]
summary: Registrar movimiento operativo (ANH 0651)
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [project_id, movement_type, volume_bbls, timestamp]
properties:
project_id:
type: string
format: uuid
movement_type:
type: string
enum: [production, transfer, injection, flare, loss]
volume_bbls:
type: number
format: double
timestamp:
type: string
format: date-time
notes:
type: string
responses:
'201':
description: Movimiento registrado
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
/downtime:
get:
tags: [Operations]
summary: Listar paros operativos registrados
parameters:
- name: project_id
in: query
schema:
type: string
format: uuid
responses:
'200':
description: Lista de paros operativos
content:
application/json:
schema:
type: array
items:
type: object
'401':
$ref: '#/components/responses/Unauthorized'
post:
tags: [Operations]
summary: Registrar paro operativo
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [project_id, reason, started_at]
properties:
project_id:
type: string
format: uuid
reason:
type: string
started_at:
type: string
format: date-time
ended_at:
type: string
format: date-time
nullable: true
responses:
'201':
description: Paro registrado
'401':
$ref: '#/components/responses/Unauthorized'
/well-tests:
get:
tags: [Operations]
summary: Listar pruebas de pozo registradas
parameters:
- name: project_id
in: query
schema:
type: string
format: uuid
responses:
'200':
description: Lista de pruebas de pozo
content:
application/json:
schema:
type: array
items:
type: object
'401':
$ref: '#/components/responses/Unauthorized'
post:
tags: [Operations]
summary: Registrar prueba de pozo
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [project_id, asset_id, test_date]
properties:
project_id:
type: string
format: uuid
asset_id:
type: string
format: uuid
test_date:
type: string
format: date-time
oil_rate_bopd:
type: number
water_cut_pct:
type: number
gor_scf_bbl:
type: number
responses:
'201':
description: Prueba de pozo registrada
'401':
$ref: '#/components/responses/Unauthorized'
/lab-results:
get:
tags: [Operations]
summary: Listar resultados de laboratorio
parameters:
- name: project_id
in: query
schema:
type: string
format: uuid
responses:
'200':
description: Lista de resultados de laboratorio
content:
application/json:
schema:
type: array
items:
type: object
'401':
$ref: '#/components/responses/Unauthorized'
post:
tags: [Operations]
summary: Registrar resultado de análisis de laboratorio
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [project_id, sample_date, api_gravity]
properties:
project_id:
type: string
format: uuid
sample_date:
type: string
format: date-time
api_gravity:
type: number
bsw_pct:
type: number
viscosity_cp:
type: number
responses:
'201':
description: Resultado registrado
'401':
$ref: '#/components/responses/Unauthorized'
/meters/readings:
get:
tags: [Operations]
summary: Listar lecturas de medidores de flujo
parameters:
- name: project_id
in: query
schema:
type: string
format: uuid
responses:
'200':
description: Lista de lecturas de medidores
content:
application/json:
schema:
type: array
items:
type: object
'401':
$ref: '#/components/responses/Unauthorized'
post:
tags: [Operations]
summary: Registrar lectura de medidor de flujo
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [project_id, meter_id, reading_value, read_at]
properties:
project_id:
type: string
format: uuid
meter_id:
type: string
format: uuid
reading_value:
type: number
read_at:
type: string
format: date-time
unit:
type: string
example: "BBL"
responses:
'201':
description: Lectura registrada
'401':
$ref: '#/components/responses/Unauthorized'
# ── Billing ───────────────────────────────────────────────────────────────
/billing/usage:
get:
tags: [Billing]
summary: Consultar uso actual y costo estimado del proyecto
parameters:
- name: project_id
in: query
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Datos de uso y facturación
content:
application/json:
schema:
$ref: '#/components/schemas/BillingUsage'
'401':
$ref: '#/components/responses/Unauthorized'
/billing/invoices:
get:
tags: [Billing]
summary: Listar historial de facturas del proyecto
parameters:
- name: project_id
in: query
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Lista de facturas
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Invoice'
'401':
$ref: '#/components/responses/Unauthorized'
/billing/admin/summary:
get:
tags: [Billing]
summary: Resumen de revenue global (solo superadmin)
responses:
'200':
description: Resumen de facturación global
content:
application/json:
schema:
type: object
properties:
total_revenue_usd:
type: number
active_subscriptions:
type: integer
mrr_usd:
type: number
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
/billing/admin/subscriptions/{project_id}:
put:
tags: [Billing]
summary: Actualizar plan de suscripción de un proyecto
parameters:
- name: project_id
in: path
required: true
schema:
type: string
format: uuid
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [plan]
properties:
plan:
type: string
enum: [starter, professional, enterprise]
responses:
'200':
description: Suscripción actualizada
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
/billing/invoices/{id}/status:
patch:
tags: [Billing]
summary: Actualizar estado de una factura
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [status]
properties:
status:
type: string
enum: [pending, paid, overdue, cancelled]
responses:
'200':
description: Estado actualizado
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
/billing/snapshots:
post:
tags: [Billing]
summary: Tomar snapshot manual del uso actual para facturación
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [project_id]
properties:
project_id:
type: string
format: uuid
responses:
'201':
description: Snapshot registrado
'401':
$ref: '#/components/responses/Unauthorized'
# ── Edge Projects ─────────────────────────────────────────────────────────
/edge/projects:
get:
tags: [Edge Projects]
summary: Listar proyectos edge del usuario autenticado
responses:
'200':
description: Lista de proyectos edge
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/EdgeProject'
'401':
$ref: '#/components/responses/Unauthorized'
post:
tags: [Edge Projects]
summary: Crear nuevo proyecto edge
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateProjectRequest'
responses:
'201':
description: Proyecto creado
content:
application/json:
schema:
$ref: '#/components/schemas/EdgeProject'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
/edge/projects/{id}:
get:
tags: [Edge Projects]
summary: Obtener detalles de un proyecto edge
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Detalles del proyecto
content:
application/json:
schema:
$ref: '#/components/schemas/EdgeProject'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
put:
tags: [Edge Projects]
summary: Actualizar proyecto edge
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateProjectRequest'
responses:
'200':
description: Proyecto actualizado
content:
application/json:
schema:
$ref: '#/components/schemas/EdgeProject'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
delete:
tags: [Edge Projects]
summary: Eliminar proyecto edge y todos sus recursos
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'204':
description: Proyecto eliminado
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
/edge/projects/{id}/full:
get:
tags: [Edge Projects]
summary: Obtener configuración completa del proyecto (assets, devices, variables)
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Configuración completa del proyecto
content:
application/json:
schema:
type: object
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
/edge/projects/{id}/installer:
get:
tags: [Edge Projects]
summary: Descargar instalador MSI del agente edge para Windows
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Archivo MSI del instalador
content:
application/octet-stream:
schema:
type: string
format: binary
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
/edge/projects/{id}/deploy:
post:
tags: [Edge Projects]
summary: Desplegar configuración actualizada al agente edge via MQTT
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Despliegue iniciado
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
/edge/projects/{id}/rebuild:
post:
tags: [Edge Projects]
summary: Reconstruir instalador MSI del agente edge
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'202':
description: Rebuild encolado
'401':
$ref: '#/components/responses/Unauthorized'
/edge/projects/{id}/linux-install:
get:
tags: [Edge Projects]
summary: Obtener script de instalación del agente para Linux
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Script bash de instalación
content:
text/x-shellscript:
schema:
type: string
'401':
$ref: '#/components/responses/Unauthorized'
/edge/agent/linux-binary/{arch}:
get:
tags: [Edge Projects]
summary: Descargar binario del agente edge para Linux
parameters:
- name: arch
in: path
required: true
schema:
type: string
enum: [x86_64, aarch64]
responses:
'200':
description: Binario compilado del agente
content:
application/octet-stream:
schema:
type: string
format: binary
'401':
$ref: '#/components/responses/Unauthorized'
/edge/projects/{project_id}/deployments:
get:
tags: [Edge Projects]
summary: Listar historial de deployments del proyecto
parameters:
- name: project_id
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Lista de deployments
content:
application/json:
schema:
type: array
items:
type: object
'401':
$ref: '#/components/responses/Unauthorized'
/edge/projects/{project_id}/build:
post:
tags: [Edge Projects]
summary: Disparar build del instalador del proyecto
parameters:
- name: project_id
in: path
required: true
schema:
type: string
format: uuid
responses:
'202':
description: Build encolado
'401':
$ref: '#/components/responses/Unauthorized'
# ── Edge Assets ───────────────────────────────────────────────────────────
/edge/projects/{project_id}/assets:
get:
tags: [Edge Assets]
summary: Listar assets (pozos, tanques) del proyecto
parameters:
- name: project_id
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Lista de assets del proyecto
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/EdgeAsset'
'401':
$ref: '#/components/responses/Unauthorized'
/edge/assets:
post:
tags: [Edge Assets]
summary: Crear nuevo asset en un proyecto
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateAssetRequest'
responses:
'201':
description: Asset creado
content:
application/json:
schema:
$ref: '#/components/schemas/EdgeAsset'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
/edge/assets/{id}:
get:
tags: [Edge Assets]
summary: Obtener detalles de un asset
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Detalles del asset
content:
application/json:
schema:
$ref: '#/components/schemas/EdgeAsset'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
put:
tags: [Edge Assets]
summary: Actualizar asset
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateAssetRequest'
responses:
'200':
description: Asset actualizado
content:
application/json:
schema:
$ref: '#/components/schemas/EdgeAsset'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
delete:
tags: [Edge Assets]
summary: Eliminar asset y sus dispositivos
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'204':
description: Asset eliminado
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
# ── Edge Devices ──────────────────────────────────────────────────────────
/edge/assets/{asset_id}/devices:
get:
tags: [Edge Devices]
summary: Listar dispositivos de campo del asset (PLCs, RTUs, etc.)
parameters:
- name: asset_id
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Lista de dispositivos
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/EdgeDevice'
'401':
$ref: '#/components/responses/Unauthorized'
post:
tags: [Edge Devices]
summary: Crear nuevo dispositivo de campo en el asset
parameters:
- name: asset_id
in: path
required: true
schema:
type: string
format: uuid
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateDeviceRequest'
responses:
'201':
description: Dispositivo creado
content:
application/json:
schema:
$ref: '#/components/schemas/EdgeDevice'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
/edge/devices/{id}:
put:
tags: [Edge Devices]
summary: Actualizar configuración de un dispositivo
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateDeviceRequest'
responses:
'200':
description: Dispositivo actualizado
content:
application/json:
schema:
$ref: '#/components/schemas/EdgeDevice'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
delete:
tags: [Edge Devices]
summary: Eliminar dispositivo y sus variables
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'204':
description: Dispositivo eliminado
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
# ── Edge Variables ────────────────────────────────────────────────────────
/edge/devices/{device_id}/variables:
get:
tags: [Edge Variables]
summary: Listar variables/tags configuradas en el dispositivo
parameters:
- name: device_id
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Lista de variables
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/EdgeVariable'
'401':
$ref: '#/components/responses/Unauthorized'
post:
tags: [Edge Variables]
summary: Crear variable/tag en el dispositivo
parameters:
- name: device_id
in: path
required: true
schema:
type: string
format: uuid
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateVariableRequest'
responses:
'201':
description: Variable creada
content:
application/json:
schema:
$ref: '#/components/schemas/EdgeVariable'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
/edge/variables/{id}:
put:
tags: [Edge Variables]
summary: Actualizar variable/tag del dispositivo
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateVariableRequest'
responses:
'200':
description: Variable actualizada
content:
application/json:
schema:
$ref: '#/components/schemas/EdgeVariable'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
delete:
tags: [Edge Variables]
summary: Eliminar variable/tag del dispositivo
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'204':
description: Variable eliminada
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
# ── Edge Agents ───────────────────────────────────────────────────────────
/edge/agents/logs:
get:
tags: [Edge Projects]
summary: Consultar logs del agente edge (filtrado por proyecto)
parameters:
- name: project_id
in: query
required: true
schema:
type: string
format: uuid
- name: level
in: query
schema:
type: string
enum: [debug, info, warn, error]
- name: limit
in: query
schema:
type: integer
default: 100
responses:
'200':
description: Lista de entradas de log
content:
application/json:
schema:
type: array
items:
type: object
properties:
id:
type: string
format: uuid
level:
type: string
category:
type: string
message:
type: string
timestamp:
type: string
format: date-time
'401':
$ref: '#/components/responses/Unauthorized'
post:
tags: [Edge Projects]
summary: Enviar entrada de log desde el agente edge (uso interno)
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [project_id, level, message]
properties:
project_id:
type: string
format: uuid
level:
type: string
enum: [debug, info, warn, error]
category:
type: string
message:
type: string
responses:
'201':
description: Log registrado
'401':
$ref: '#/components/responses/Unauthorized'
/edge/agents/{project_id}/health:
get:
tags: [Edge Projects]
summary: Obtener estado de salud del agente edge de un proyecto
parameters:
- name: project_id
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Estado de salud del agente
content:
application/json:
schema:
type: object
properties:
status:
type: string
enum: [online, offline, degraded]
last_seen:
type: string
format: date-time
uptime_seconds:
type: integer
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
# ── WebSocket ─────────────────────────────────────────────────────────────
/ws/telemetry:
get:
tags: [Telemetry]
summary: Stream WebSocket de telemetría en tiempo real
description: |
Conectar al stream de telemetría en tiempo real vía WebSocket.
Los browsers deben pasar el JWT como query param `token` ya que
no pueden enviar el header Authorization en conexiones WebSocket.
parameters:
- name: project_id
in: query
schema:
type: string
format: uuid
description: Filtrar eventos por proyecto
- name: token
in: query
required: true
schema:
type: string
description: JWT Bearer token para autenticación WebSocket
responses:
'101':
description: Switching Protocols — conexión WebSocket establecida
'401':
description: Token inválido o ausente
# ── System ────────────────────────────────────────────────────────────────
/health:
get:
tags: [System]
summary: Estado de salud detallado del sistema
security: []
servers:
- url: http://localhost:8000
description: Direct (no /api prefix)
responses:
'200':
description: Estado del sistema
content:
application/json:
schema:
$ref: '#/components/schemas/HealthResponse'
/metrics:
get:
tags: [System]
summary: Métricas Prometheus (uso interno — no expuesto públicamente)
security: []
servers:
- url: http://localhost:8000
description: Direct (no /api prefix)
responses:
'200':
description: Métricas en formato Prometheus text
content:
text/plain:
schema:
type: string
components:
responses:
Unauthorized:
description: Token JWT ausente o inválido
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
Forbidden:
description: Permisos insuficientes para esta operación
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
NotFound:
description: Recurso no encontrado
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
BadRequest:
description: Datos de entrada inválidos
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'